Table of Contents |
---|
Overview
ClinSpark supports customer-provided dynamic logic to perform calculations and validations at the time of data collection. Methods support setting itemData values dynamically based on arbitrary logic and subject data. Edit Checks support dynamic validation of individual itemData values.
The functionality is extremely powerful. It also requires a certain degree of technical skill in Javascript, and an understanding of the context within ClinSpark that this logic executes.
CDISC Standard Background
Much of the CDISC ODM defines a schema for structuring data, and this is of course the foundation for ClinSpark’s data and object model. In addition to this static data structure guidance, the 1.3.0 CDISC ODM specification also defines a number of optional elements which support specifying dynamic behaviors for the model. This addition supports powerful use cases beyond what is possible with purely static data structure definitions. The ODM specification and it identifies 3 types that may be supported:
Name | Description | Supported by ClinSpark? | Where Defined in CDISC ODM Spec |
---|---|---|---|
MethodDef | A MethodDef describes how a data value can be obtained from a collection of other data values. This allows calculated or derived values to be dynamically generated during data capture. | Yes | Section 3.1.1.3.9 |
RangeCheck | A RangeCheck defines a constraint on the value of the enclosing item. It may defined as an explicit 1 or 2 sided range or as an expression that evaluates to True when the ItemData value is valid or False when the ItemData value is invalid. | Yes. We call them “Edit Checks” | Section 3.1.1.3.6.4 |
ConditionDef | Indicates that the referenced element may be omitted if the Condition evaluates to true. | No | Section 3.1.1.3.11 |
The ODM specification does not define how to implement these behaviors. It defines data fields called Formal Expressions to hold computer code and specifies inputs and outputs. But it does not require or even suggest an implementation model. It is up to the implementation to define how this Formal Expression instruction gets executed to perform the desired operation.
In light of this, ClinSpark has implemented the MethodDef and RangeCheck to leverage Formal Expressions written in Javascript. This Javascript code has access at execution time to the ItemData and its FormData context in the form of a JSON object. And this execution occurs on the server prior to persisting the data.
Methods vs Edit Checks
Methods are top-level CRF Design elements which can be attached to zero to many items, and the value returned by a Method is set as the value of the attached ItemData. Edit Checks are essentially validation scripts, and the return value can determine whether an ItemData can be Complete or nonconformant. Both Methods and Edit Checks execute once each time data is collected.
Type | Reusable within a study? | Outcome | Return Type | In CDISC |
---|---|---|---|---|
Method | Yes. Can be attached to multiple Items | Sets the value of the attached ItemData | Return type MUST match the datatype of the attached item. | MethodDef |
Edit Check | No. Must be copy/pasted each time onto the appropriate Item | Decides whether an Item is conformant | Boolean. True = valid value, False = invalid. | RangeCheck |
Formal Expressions are Javascript
ClinSpark supports “Formal Expressions” through Javascript. This is the Javascript code which gets executed to accomplish the task. It is the contents of a method, which makes some calculation, performs whatever logic is required to implement either the MethodDef or the RangeCheck.
...
The return requirements for a Method are a value if the function does not need units, or a JSON object as shown above if units must be specified.
Formal Expression Dev Tooling in ClinSpark
Note that this tooling was introduced in ClinSpark 1.5.2.
...
Here is an overview of the editor and testing harness:
...
Red dot | Description |
---|---|
1 | This is an embedded Javascript editor. It supports appropriate highlighting and basic syntax checking. Note the warnings and errors in the left sidebar. |
2 | Guidance on using the logger function to gain insight into the inner workings of your code. This is crucial during development and troubleshooting. |
3 | To execute the expression against collected itemData, this field accepts the database ID of an itemData. Details to follow on how to obtain this value. |
4 | Executes the method from #1 against the itemData specified in #3 and displays the output along with any logger content. This allows testing and troubleshooting against actual data, without requiring additional collection activities. |
5 | Displays the available ClinSpark-provided Javascript functions which will be available to the expression at runtime. |
6 | Downloads the JSON contents of the itemData from #3 as a file. This may be useful, but note that you can also use logger(JSON.stringify(itemJson, null, 2)); and also logger(JSON.stringify(formJson, null, 2)); to see this output dynamically as well. |
Obtaining ItemData IDs for testing execution
Expressions are run against actual itemData. The test harness requires as an input the database ID of a specific itemData of the user’s choice.
The first step in testing a method is to ensure that there is some collected itemData to test against. Once this is done, follow this walkthrough to see how to obtain the desired ID.
...
Red dot | Description |
---|---|
1 | Search for a collected itemData which you would like to run your function against. |
2 | Hover over the name link. |
3 | Here you will see a numeric ID for that itemData you’re hovering over. THIS is the database ID, and this is what you can place into the ItemData ID field in the test harness. |
Implicit Methods
As part of ClinSpark’s configuration, commonly useful utility methods are available to your code at runtime. These will be visible to your code at execution time and you can call them directly as needed. These methods assist in finding a certain item from a form for instance, ClinSpark-specific date utilities, etc. #5 from above will open a page showing all available methods.
...
Code Block |
---|
function findFirstItemByName(formJson, itemName, sasFieldName, itemGroupRepeatKey) { var itemGroups = formJson.form.itemGroups; if (itemGroups && itemGroups.length) { for (var i = 0; i < itemGroups.length; i++) { var itemGroup = itemGroups[i]; logger('inspecting itemGroup.name'+itemGroup.name); |
Implicit Data
Two JSON objects are available to each expression execution, itemJson and formJson. The itemJson contains all of the data and metadata pertaining to the itemData which the expression is attached to. The formData is the complete formData graph, including all contained itemGroupData and itemData as wll as context info such as data about the subject/volunteer, cohort, study event etc.
You can use the logger to see the contents of these objects like this:
...
Red dot | Description |
---|---|
1 | As per the highlighted help, add a logger entry to print the JSON object (itemJson or formJson) as a string. |
2 | Enter your chosen itemData target. |
3 | Click “Test” |
The execution window output now displays the JSON object.
...
Code Block |
---|
{ "item": { "name": "VS_Significance", "dataType": "string", "dataCollectionStatus": "Unsaved", "sasFieldName": "VSCLSIG", "codeListItems": [ { "codedValue": "CS", "decode": "Clinically Significant" }, { "codedValue": "NCS", "decode": "Not Clinically Significant" } ], "measurementUnits": [], "measurementUnit": null, "value": null, "outOfRange": false, "nonconformantMessage": null, "length": null, "significantDigits": null, "canceled": false } } |
formJson
The formJson object contains the complete formData along with all contained itemGroupData and their child itemData.
...
findFormData supports querying for previously collected formData of this same subject only.
Guidelines and Suggestions
Define variables up top to support reusability across studies. See the examples in the Method Library section of this help site.