Skip to content

Commit

Permalink
Merge pull request #44 from bcbi/dev
Browse files Browse the repository at this point in the history
Add docstrings
  • Loading branch information
AshlinHarris authored Sep 29, 2024
2 parents b5cbc00 + 60f7651 commit 6596a40
Show file tree
Hide file tree
Showing 22 changed files with 854 additions and 185 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "REDCap"
uuid = "ba918724-fbf9-5e4a-a61c-87e95654e718"
authors = ["Cory Cothrum", "Dilum Aluthge <[email protected]>", "Ashlin Harris <[email protected]>"]
version = "2.4.0"
version = "2.5.0"

[deps]
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
Expand Down
105 changes: 8 additions & 97 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,112 +6,23 @@ REDCap.jl is an API wrapper for REDCap v14, written in Julia.

## Examples
```julia
using REDCap
pkg> activate --temp; add REDCap
julia> using REDCap

export_version()
julia> export_version()

project_token = create_project(
julia> project_token = create_project(
data = (project_title = "Test Project", purpose = 0),
odm = "Data_Dictionary.xml")

import_records(token=project_token, data="example.csv", format=:csv)
julia> import_records(token=project_token, data="example.csv", format=:csv)

delete_records(token=project_token, records=[2,3])
julia> delete_records(token=project_token, records=[2,3])

export_logging(token=project_token)
julia> export_logging(token=project_token)
```

## Syntax
Each REDCap method accepts a number of parameters that follow a shared naming convention.
REDCap.jl is designed to closely follow the design and syntax patterns of REDCap.
Every REDCap API method is available as a function that supplies certain required parameters and checks user inputs for validity.

Return values and REDCap messages are returned as Strings directly, but the documentation shows how these can be parsed in useful ways.

Function arguments are named after REDCap method parameters.
These are passed as named arguments and take values with intuitive types.

### `token` and `url`
Nearly all REDCap methods accept a token that is unique to the project and user.

The URL must exactly match this example:
```https://example.example/redcap/api/```

Your REDCap token and your institution's REDCap API URL can be read by default from Julia's environment variables.
You can make them avaiable to REDCap.jl by putting the following lines in [your local Julia startup file](https://docs.julialang.org/en/v1/manual/command-line-interface/#Startup-file) (probably `~/.julia/config/startup.jl`):
```julia
ENV["REDCAP_API_TOKEN"] = "C0FFEEC0AC0AC0DEC0FFEEC0AC0AC0DE"
ENV["REDCAP_API_URL"] = "http://example.com/redcap/api/"
```
They can also be passed as ordinary named arguments.

A few methods accept a super token, including `create_project`, which can be used to generate a project and project-level token.
If you have a super token, you might wish to keep that in your startup file, generating and saving project-level tokens as needed.

### `data`
The `data` parameter contains a list of attributes.
In REDCap.jl, this can be a NamedTuple (or any derived type), a file handle, or a String.
If you use a NamedTuple, it will be translated internally into whatever `format` you use (xml by default).
```julia
import_project_info(
data=(
project_title="New name",
project_notes="New notes"
),
returnFormat=:csv,
)
```
But please keep in mind that a NamedTuple with one value must contain a comma:
```julia
import_project_info(
data=(
project_title="New name", # this comma is required
),
returnFormat=:csv,
)
```
A `Dict` value is fine as well.
```julia
import_project_info(data=Dict(:project_title=>"New name"), returnFormat=:csv)
```
String values are accepted. If the string is a file name, the contents of the file are sent; otherwise, the value is sent directly as part of the API request.
```julia
data_string = """
[{"data_access_group_name":"CA Site","unique_group_name":"ca_site"},
{"data_access_group_name":"FL Site","unique_group_name":"fl_site"},
{"data_access_group_name":"New Site","unique_group_name":""}]
"""
out = open("data_file.json","w"); write(out, data_string); close(out)

import_DAGs(token=t,data=data_string, format=:json) # string is passed to the API

import_DAGs(token=t, data="data_file.json", format=:json) # string is pattern-matched as a filename

```
As for collections, only collections of scalar entries are currently supported.
So, a list of attributes and values is accepted, but a Dict containing multiple rows per column can only be read in from a file.

In the REDCap API, the presence of a `data` parameter often changes the behavior of a method.
For instance, most import methods are implemented as an export method with an added data parameter.
In REDCap.jl, it would be considered a bug for `import_project_data` to ever act as `export_project_data`, so the data paramater is almost always required where it is present.

### `format` and `returnFormat`
Supported options are `:csv`, `:json`, `:xml` (the default value), and sometimes `:odm`.
These values can be passed as Strings or Symbols.

Generally, the `format` parameter designates user input and the `returnFormat` parameter applies to REDCap messages and return values.
However, this is not consistent within REDCap.
REDCap.jl functions are designed to not accept any parameters that have no effect on the result.

### `content` and `action`
The `content` and `action` parameters are what define each REDCap method, for the most part.
In REDCap.jl, these are passed internally and don't need to be supplied by the user.
Instead, they're fixed for each function.

## Troubleshooting
If a function call doesn't produce the expected results, try making debug messages visible for this package by running `ENV["JULIA_DEBUG"] = REDCap`.
The data parameter is converted to a formatted string, so you might try different format parameters (`:csv`, `:json`, or `:xml`).
Feel free to create an issue for any unexpected errors, or for feature requests.
For more details, see the internal documentation (`help?> REDCap`).

## Acknowledgments
The contributors are grateful for the support of Mary McGrath, Paul Stey, Fernando Gelin, the Brown Data Science Institute, the Brown Center for Biomedical Informatics, and the Tufts CTSI Informatics team.
17 changes: 17 additions & 0 deletions src/API_Methods.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
include("api_methods/arms.jl")
include("api_methods/data_access_groups.jl")
include("api_methods/events.jl")
include("api_methods/field_names.jl")
include("api_methods/files.jl")
include("api_methods/file_repository.jl")
include("api_methods/instruments.jl")
include("api_methods/logging.jl")
include("api_methods/metadata.jl")
include("api_methods/projects.jl")
include("api_methods/records.jl")
include("api_methods/redcap.jl")
include("api_methods/repeating_instruments_and_events.jl")
include("api_methods/reports.jl")
include("api_methods/surveys.jl")
include("api_methods/users.jl")
include("api_methods/user_roles.jl")
122 changes: 105 additions & 17 deletions src/REDCap.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,107 @@
#TODO: some import methods can be used to delete entries
# Should this behavior be migrated to delete_* functions?

"""
REDCap API methods are defined as named Julia functions.
The `content` and `action` parameters are managed internally.
Function docstrings indicate how to use the Julia functions.
Refer to the official documentation for detailed specifications on the corresponding REDCap methods.
## Syntax
Each REDCap method accepts a number of parameters that follow a shared naming convention.
REDCap.jl is designed to closely follow the design and syntax patterns of REDCap.
Every REDCap API method is available as a function that supplies certain required parameters and checks user inputs for validity.
Return values and REDCap messages are returned as Strings directly, but the documentation shows how these can be parsed in useful ways.
Function arguments are named after REDCap method parameters.
These are passed as named arguments and take values with intuitive types.
### `token` and `url`
Nearly all REDCap methods accept a token that is unique to the project and user.
The URL must exactly match this example:
```https://example.example/redcap/api/```
Your REDCap token and your institution's REDCap API URL can be read by default from Julia's environment variables.
You can make them avaiable to REDCap.jl by putting the following lines in [your local Julia startup file](https://docs.julialang.org/en/v1/manual/command-line-interface/#Startup-file) (probably `~/.julia/config/startup.jl`):
```julia
ENV["REDCAP_API_TOKEN"] = "C0FFEEC0AC0AC0DEC0FFEEC0AC0AC0DE"
ENV["REDCAP_API_URL"] = "http://example.com/redcap/api/"
```
They can also be passed as ordinary named arguments.
A few methods accept a super token, including `create_project`, which can be used to generate a project and project-level token.
If you have a super token, you might wish to keep that in your startup file, generating and saving project-level tokens as needed.
### `data`
The `data` parameter contains a list of attributes, which varies between REDCap methods.
For definitive attribute lists, see the official REDCap documentation.
In REDCap.jl, this can be a NamedTuple (or any derived type), a file handle, or a String.
If you use a NamedTuple, it will be translated internally into whatever `format` you use (xml by default).
```julia
import_project_info(
data=(
project_title="New name",
project_notes="New notes"
),
returnFormat=:csv,
)
```
But please keep in mind that a NamedTuple with one value must contain a comma:
```julia
import_project_info(
data=(
project_title="New name", # this comma is required
),
returnFormat=:csv,
)
```
A `Dict` value is fine as well.
```julia
import_project_info(data=Dict(:project_title=>"New name"), returnFormat=:csv)
```
String values are accepted. If the string is a file name, the contents of the file are sent; otherwise, the value is sent directly as part of the API request.
```julia
data_string = \"\"\"
[{"data_access_group_name":"CA Site","unique_group_name":"ca_site"},
{"data_access_group_name":"FL Site","unique_group_name":"fl_site"},
{"data_access_group_name":"New Site","unique_group_name":""}]
\"\"\"
out = open("data_file.json","w"); write(out, data_string); close(out)
import_DAGs(token=t,data=data_string, format=:json) # string is passed to the API
import_DAGs(token=t, data="data_file.json", format=:json) # string is pattern-matched as a filename
```
As for collections, only collections of scalar entries are currently supported.
So, a list of attributes and values is accepted, but a Dict containing multiple rows per column can only be read in from a file.
In the REDCap API, the presence of a `data` parameter often changes the behavior of a method.
For instance, most import methods are implemented as an export method with an added data parameter.
In REDCap.jl, it would be considered a bug for `import_project_data` to ever act as `export_project_data`, so the data paramater is almost always required where it is present.
### `format` and `returnFormat`
Supported options are `:csv`, `:json`, `:xml` (the default value), and sometimes `:odm`.
These values can be passed as Strings or Symbols.
For import methods, the `format` parameter designates user input and the `returnFormat` parameter applies to REDCap messages and return values.
Otherwise, there is generally only a single `format` parameter that applies to REDCap messages and return values.
### `content` and `action`
The `content` and `action` parameters are what define each REDCap method, for the most part.
In REDCap.jl, these are passed internally and don't need to be supplied by the user.
Instead, they're fixed for each function.
## Troubleshooting
- If a function call doesn't produce the expected results, try making debug messages visible for this package by running `ENV["JULIA_DEBUG"] = REDCap`.
- The `data` parameter is converted to a formatted string, so you might try different format parameters (`:csv`, `:json`, or `:xml`).
If you're uncertain about the format for an input data parameter, try setting up an example in the browser and exporting the data.
You may have to remove the values for certain generated columns before importing.
- Feel free to create an issue for any unexpected errors, or for feature requests.
"""
module REDCap

using Dates
Expand All @@ -10,22 +114,6 @@ include("types.jl")
include("export.jl")
include("request.jl")
include("utils.jl")
include("api_methods/arms.jl")
include("api_methods/data_access_groups.jl")
include("api_methods/events.jl")
include("api_methods/field_names.jl")
include("api_methods/files.jl")
include("api_methods/file_repository.jl")
include("api_methods/instruments.jl")
include("api_methods/logging.jl")
include("api_methods/metadata.jl")
include("api_methods/projects.jl")
include("api_methods/records.jl")
include("api_methods/redcap.jl")
include("api_methods/repeating_instruments_and_events.jl")
include("api_methods/reports.jl")
include("api_methods/surveys.jl")
include("api_methods/users.jl")
include("api_methods/user_roles.jl")
include("API_Methods.jl")

end
64 changes: 51 additions & 13 deletions src/api_methods/arms.jl
Original file line number Diff line number Diff line change
@@ -1,28 +1,66 @@
#TODO: consider changing all functions to the following form:
#=
function delete_arms(;kwargs...)
REDCap.request(;kwargs...)
end
=#
"""
function delete_arms(; url=get_url(), token=get_token(), arms,)
Delete study arms from a REDCap project
# Notes
If no value is provided for `arms`, all study arms are returned.
Deleting a study arm deletes any included records and data.
function delete_arms(; url=get_url(), token=get_token(), arms=nothing,)
# Named arguments
- `url`: (read from `ENV["REDCAP_API_URL"]` by default)
- `token`: an API token specific to the REDCap project and username (read from `ENV["REDCAP_API_TOKEN"]` by default)
- `arms`: names of study arms (can be scalar or vector)
"""
function delete_arms(; url=get_url(), token=get_token(), arms,)
REDCap.request(;
url=REDCap_url(url),
kwargs = (;token=REDCap_token(token), content=:arm, action=:import, arms,),
)
end

function export_arms(; url=get_url(), token=get_token(), format=nothing, returnFormat=nothing, arms=nothing,)
"""
function export_arms(; url=get_url(), token=get_token(), format=nothing, returnFormat=nothing, arms=nothing,)
Export a REDCap project's study arms
# Notes
If no value is provided for `arms`, all study arms are returned.
# Named arguments
- `url`: (read from `ENV["REDCAP_API_URL"]` by default)
- `token`: an API token specific to the REDCap project and username (read from `ENV["REDCAP_API_TOKEN"]` by default)
- `format`: the desired output format: `:csv`, `:json`, or `:xml` (default)
- `arms`: names of study arms (can be scalar or vector)
"""
function export_arms(; url=get_url(), token=get_token(), format=nothing, arms=nothing,)
REDCap.request(;
url=REDCap_url(url),
kwargs = (; token=REDCap_token(token), content=:arm, format=REDCap_format(format), arms, returnFormat=REDCap_format(returnFormat),),
kwargs = (; token=REDCap_token(token), content=:arm, format=REDCap_format(format), arms,),
)
end

#All examples use JSON
#TODO: what is the proper format for multi-item XML? I can't find this anywhere...
# #TODO: is this ata paratamet required, given the action parameter?
function import_arms(; url=get_url(), token=get_token(), format=nothing, data=nothing, returnFormat=nothing, override=nothing,)
"""
function import_arms(; url=get_url(), token=get_token(), format=nothing, data, returnFormat=nothing, override=nothing,)
Import new study arms to a REDCap project, or rename existing arms
# Notes
Deleting a study arm deletes any included records and data.
# Named arguments
- `url`: (read from `ENV["REDCAP_API_URL"]` by default)
- `token`: an API token specific to the REDCap project and username (read from `ENV["REDCAP_API_TOKEN"]` by default)
- `arms`: names of study arms (can be scalar or vector)
- `override`: if true, all existing arms are erased; if false (default), existing arms can only be renamed
- `data`: May be a String, a file name, or a data type such as NamedTuple or Dict
- `format`: the format of the `data` input parameter: `:csv`, `:json`, or `:xml` (default). If `data` is a String or a file name, this value must indicate the correct format. If `data` is a NamedTuple, Dict, or similar type, this value will determine what format will be used internally to pass on the data.
- `returnFormat`: the desired output format: `:csv`, `:json`, or `:xml` (default)
"""
function import_arms(; url=get_url(), token=get_token(), format=nothing, data, returnFormat=nothing, override=nothing,)
REDCap.request(;
url=REDCap_url(url),
data=REDCap_data(data,REDCap_format(format),xml_tag="arms"),
Expand Down
Loading

2 comments on commit 6596a40

@AshlinHarris
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/116274

Tip: Release Notes

Did you know you can add release notes too? Just add markdown formatted text underneath the comment after the text
"Release notes:" and it will be added to the registry PR, and if TagBot is installed it will also be added to the
release that TagBot creates. i.e.

@JuliaRegistrator register

Release notes:

## Breaking changes

- blah

To add them here just re-invoke and the PR will be updated.

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v2.5.0 -m "<description of version>" 6596a402e83ab58988b218bcc2683244e4c6bc42
git push origin v2.5.0

Please sign in to comment.