Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Using the Castor EDC API

notebook/getting_started.livemd

Using the Castor EDC API

# ## Fix for the free tier of Fly.io livebook
# ## Allocate 2GB swap
# System.cmd("fallocate", ["-l", "2G", "/swap"])
# System.cmd("chmod", ["400", "/swap"])
# System.cmd("mkswap", ["/swap"])
# System.cmd("swapon", ["/swap"])

Mix.install([
  {:ex_castor_edc, "~> 0.4.0"},
  {:kino, "~> 0.6.1"}
])

Introduction

This notebook serves as an introduction on how to use the API wrapper. It’s not meant to be an exhaustive guide.

Authentication

Before we can start making requests to the API we need to have a client_id and a client_secret. You can get this information from the account settings page.

Enter your client_id and client_secret in the form below after evaluating the code-block.

endpoints = [
  {"https://data.castoredc.com/", "NL"},
  {"https://uk.castoredc.com/", "UK"},
  {"https://us.castoredc.com/", "US"},
  {"https://au.castoredc.com/", "AU"}
]

endpoint = Kino.Input.select("Region", endpoints) |> Kino.render()
client_id = Kino.Input.text("Client Id") |> Kino.render()
client_secret = Kino.Input.password("Client Secret") |> Kino.render()

Kino.nothing()

Next we need to exchange your client_id and client_secret for an access token. The access token can then be used to make other requests to the API.

client =
  CastorEDC.Client.new(
    %{
      client_id: Kino.Input.read(client_id),
      client_secret: Kino.Input.read(client_secret)
    },
    endpoint: Kino.Input.read(endpoint)
  )

{:ok, client} =
  client
  |> CastorEDC.authenticate()

client

Performing authenticated API requests

Let’s start at the beginning and fetch all of the studies that you have access to with your account.

All function calls return a 3 element tuple. The first element is the http status code, the second element is the decoded response from the API, and the last element is the raw response that came back from the server.

We’ll make extensive use of pattern-matching to collect that the data that we’re interested in, while ignoring the rest.

Most API calls support pagination but we’ll mostly focus on the first page of the results. If you want to fetch the second page it’s possible to pass the page number as a keyword option e.g. page: 2

{200, %{"_embedded" => %{"study" => studies}}, _} = CastorEDC.Common.Studies.list(client)

studies

Now that we have a list of all the studies we’ll need to pick one to dig deeper into with further API requests.

studies =
  studies
  |> Enum.map(&{&1["study_id"], &1["name"]})
  |> Enum.sort(fn {_, a}, {_, b} -> a <= b end)

study_id = Kino.Input.select("Select a study", studies)

Let’s read the chosen study into an easily accessible variable that we can reuse with other requests.

study_id = Kino.Input.read(study_id)

Sites

With a study id in our hands we can start digging, let’s start with finding out which sites the study is being conducted at!

You’ll get the option to also run write calls against the API. Make sure that you’ve chosen a test site as to not put data where it doesn’t belong.

{200, %{"_embedded" => %{"sites" => sites}}, _} = CastorEDC.Common.Sites.list(client, study_id)

sites =
  sites
  |> Enum.filter(&amp;(&amp;1["deleted"] == false))
  |> Enum.map(&amp;{&amp;1["id"], &amp;1["name"]})

site_id = Kino.Input.select("Choose your test site", sites)
site_id = Kino.Input.read(site_id)

Participants

The CastorEDC.Participants module is our gateway into fetching information about our participants. We can either fetch entire lists or fetch specific ones. With the create function we can even directly create new ones in our study.

Let’s start with fetching the first 10.

{200, %{"_embedded" => %{"participants" => participants}}, _} =
  CastorEDC.DataCollection.Participants.list(client, study_id, page_size: 10)

participants

As you can see we now have a list of all the participants enrolled in the the study. Let’s take the first one from the list and find out more about it.

[%{"id" => participant_id} | _] = participants

{200, participant, _} =
  CastorEDC.DataCollection.Participants.find(client, study_id, participant_id)

participant

It’s also possible to create new Participants via the API allowing for the enrollment of participants through external systems.

Before you evaluate the code-block below double-check the selected site, you also need participant creation permission for the given site.

{201, new_participant, _} =
  CastorEDC.DataCollection.Participants.create(client, study_id, %{site_id: site_id})

new_participant

Study Structure

It’s also possible to fetch the elements

{200, %{"_embedded" => %{"visits" => visits}}, _} =
  CastorEDC.StudyProtocol.Visits.list(client, study_id)

visits
|> Enum.sort(&amp;(&amp;1["phase_order"] <= &amp;2["phase_order"]))
{200, %{"_embedded" => %{"forms" => forms}}, _} =
  CastorEDC.StudyProtocol.Forms.list(client, study_id)

forms
|> Enum.sort(&amp;(&amp;1["step_order"] <= &amp;2["step_order"]))
{200, %{"_embedded" => %{"fields" => fields}}, _} =
  CastorEDC.StudyProtocol.Fields.list(client, study_id)

fields

Collected Data

Study Data

Fetching your study data is easy. Remember that this API call is paginated, so you’ll need to make multiple API calls to actually get everything.

As an example we’ll only fetch the first 10 data points on page 1.

{200, %{"_embedded" => %{"items" => study_data_points}}, _} =
  CastorEDC.DataCollection.DataPoints.StudyDataPointCollection.list(client, study_id,
    page: 1,
    page_size: 10
  )

study_data_points

Wrapping up

I hope that this notebook has given you a taste of how the library works. The documentation lists all of the available endpoints that you can use.