@openedc/sdk 3.9.0 → 3.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -3
- package/openedc-sdk.d.ts +15 -9
- package/openedc-sdk.js +1846 -1826
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
<div align="center">
|
|
3
3
|
<img src="https://openedc.health/logo.png" alt="OpenEDC Logo" width="48">
|
|
4
4
|
|
|
5
|
-
### SDK for the OpenEDC Health Platform
|
|
6
|
-
#### OpenEDC Health is a modular, standards-compliant platform for medical research
|
|
5
|
+
### Software Development Kit (SDK) for the OpenEDC Health Platform
|
|
6
|
+
#### OpenEDC Health is a modular, standards-compliant platform for clinical trials and medical research.
|
|
7
|
+
#### Compliant with ICH GCP and FDA CFR Part 11. Validated following GAMP 5 guidelines.
|
|
7
8
|
</div>
|
|
8
9
|
<br>
|
|
9
10
|
|
|
@@ -99,7 +100,7 @@ await subject.set({ subjectKey: "Patient-003" }).commit();
|
|
|
99
100
|
|
|
100
101
|
#### Global project scope
|
|
101
102
|
|
|
102
|
-
Instead of specifying the project per call, a global scope can be set for all upcoming statements. This works well for linear, sequential flows but should be avoided in parallel or callback-based code that handles multiple projects concurrently.
|
|
103
|
+
Instead of specifying the project per call, a global scope can be set for all upcoming statements. This works well for linear, sequential flows but should be avoided in parallel or callback-based code that handles multiple projects concurrently. This scope is assumed for the rest of the examples.
|
|
103
104
|
|
|
104
105
|
```ts
|
|
105
106
|
import { scope, Location, SubjectData } from "@openedc/sdk";
|
|
@@ -152,6 +153,23 @@ const itemData = await ItemData
|
|
|
152
153
|
.all();
|
|
153
154
|
```
|
|
154
155
|
|
|
156
|
+
#### Subject key rendering
|
|
157
|
+
|
|
158
|
+
Full subject keys (e.g., SCREENING-MS-001) are rendered dynamically to allow updates to prefix, padding, and location settings. This requires the use of certain `SubjectData` methods to compose and render full subject keys at runtime.
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
import { SubjectData } from "@openedc/sdk";
|
|
162
|
+
|
|
163
|
+
// Render one subject key dynamically
|
|
164
|
+
const subject = await SubjectData.where().first();
|
|
165
|
+
const subjectKey = await subject.getIdAsync();
|
|
166
|
+
|
|
167
|
+
// Render multiple subject keys in a loop
|
|
168
|
+
const subjects = await SubjectData.where().all();
|
|
169
|
+
const keyOptions = await SubjectData.fetchSubjectKeyOptions();
|
|
170
|
+
const subjectKeys = subjects.map(subject => subject.getId(keyOptions));
|
|
171
|
+
```
|
|
172
|
+
|
|
155
173
|
### Live queries
|
|
156
174
|
|
|
157
175
|
```ts
|
|
@@ -162,6 +180,8 @@ await SubjectData.where({ location }).all((_, subject, method) => {
|
|
|
162
180
|
```
|
|
163
181
|
|
|
164
182
|
```ts
|
|
183
|
+
import { FormStatus } from "@openedc/sdk";
|
|
184
|
+
|
|
165
185
|
// Subscribe to item data changes via the form status as proxy
|
|
166
186
|
await FormStatus.where().all(async (_, status) => {
|
|
167
187
|
const subject = await status.subject.data;
|
|
@@ -183,6 +203,8 @@ const auditTrailItemsBySubject = await ItemData.where({ subject }).audit();
|
|
|
183
203
|
### File management
|
|
184
204
|
|
|
185
205
|
```ts
|
|
206
|
+
import { FileMetadata, FileContent } from "@openedc/sdk";
|
|
207
|
+
|
|
186
208
|
// Upload a file (only readable to users with access to the subject)
|
|
187
209
|
const file = new File(["exemplary-data"], "file.txt");
|
|
188
210
|
const fileMetadata = await new FileMetadata(file, subject.reference).commit();
|
|
@@ -200,3 +222,63 @@ const downloadMetadata = await FileMetadata.where({ fileName: "file.txt" }).firs
|
|
|
200
222
|
const downloadContent = await FileContent.where({ metadata: downloadMetadata }).first();
|
|
201
223
|
const downloadURL = await downloadContent?.getUrl();
|
|
202
224
|
```
|
|
225
|
+
|
|
226
|
+
### Further examples
|
|
227
|
+
|
|
228
|
+
<details>
|
|
229
|
+
<summary>Show</summary>
|
|
230
|
+
|
|
231
|
+
#### Create custom randomization schedule
|
|
232
|
+
|
|
233
|
+
If you want to use a custom randomization schedule that is not yet supported by OpenEDC Health natively, you can use the SDK to upload your own schedule.
|
|
234
|
+
|
|
235
|
+
For this, first create the treatment groups and randomization configuration within the web application that matches your desired configuration as good as possible (e.g., selecting a simple, permuted, or stratified block randomization and choosing stratification variables).
|
|
236
|
+
|
|
237
|
+
Then, fetch the created draft randomization plan and create your schedule programmatically:
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
import { RandomizationPlan, RandomizationSchedule, StudyEventGroupDef } from "@openedc/sdk";
|
|
241
|
+
|
|
242
|
+
// First, fetch the created draft randomization plan
|
|
243
|
+
const plan = await RandomizationPlan.where().first();
|
|
244
|
+
|
|
245
|
+
// Also get the list of your study arms
|
|
246
|
+
const arms = await StudyEventGroupDef.where("armOID").notEquals(undefined).all();
|
|
247
|
+
|
|
248
|
+
// Then, generate and commit your custom randomization schedule as a batch using these parameters:
|
|
249
|
+
// new RandomizationSchedule(planReference, orderNumber, blockNumber, subjectKey, armReference).
|
|
250
|
+
// In this example, 6 entries with a block size of 3 and 2 arms with 2:1 ratio are created.
|
|
251
|
+
await RandomizationSchedule.commit([
|
|
252
|
+
new RandomizationSchedule(plan.reference, 0, 1, 1, arms[0].reference),
|
|
253
|
+
new RandomizationSchedule(plan.reference, 1, 1, 2, arms[0].reference),
|
|
254
|
+
new RandomizationSchedule(plan.reference, 2, 1, 3, arms[1].reference),
|
|
255
|
+
new RandomizationSchedule(plan.reference, 3, 2, 4, arms[1].reference),
|
|
256
|
+
new RandomizationSchedule(plan.reference, 4, 2, 5, arms[0].reference),
|
|
257
|
+
new RandomizationSchedule(plan.reference, 5, 2, 6, arms[0].reference)
|
|
258
|
+
]);
|
|
259
|
+
|
|
260
|
+
// Finally, update the plan status to be able to see and review it within OpenEDC Health
|
|
261
|
+
await plan.info.set({ status: "review" }).commit();
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
When using stratification variables, you can specify them for each randomization schedule entry. During randomization, the system will then automatically draw the next available entry for the given values of the current subject.
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
// Specify values for one or more stratification variables (please use the actual
|
|
268
|
+
// location UUID instead of munich and the actual item OID instead of gender)
|
|
269
|
+
await RandomizationSchedule.commit([
|
|
270
|
+
new RandomizationSchedule(plan.reference, 0, 1, 1, arms[0].reference).set({ stratification: {
|
|
271
|
+
locationUUID: "munich",
|
|
272
|
+
gender: "female"
|
|
273
|
+
}}),
|
|
274
|
+
new RandomizationSchedule(plan.reference, 1, 1, 2, arms[0].reference).set({ stratification: {
|
|
275
|
+
locationUUID: "munich",
|
|
276
|
+
gender: "male"
|
|
277
|
+
}}),
|
|
278
|
+
// ...
|
|
279
|
+
]);
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
If you want to start over, you can either delete the created randomization schedule within the OpenEDC Health application or execute `await RandomizationSchedule.where({ plan }).delete();` using the SDK.
|
|
283
|
+
|
|
284
|
+
</details>
|
package/openedc-sdk.d.ts
CHANGED
|
@@ -231,8 +231,8 @@ declare class DataBlock {
|
|
|
231
231
|
refresh?: boolean;
|
|
232
232
|
}, transaction?: string): Promise<this>;
|
|
233
233
|
static commit(data: DataBlock[], project?: string, reasons?: Record<string, string>, refresh?: boolean): Promise<void>;
|
|
234
|
-
protected beforeTrigger(): Promise<void> | void;
|
|
235
|
-
protected afterTrigger(): Promise<void> | void;
|
|
234
|
+
protected beforeTrigger(changes: Record<string, unknown>): Promise<void> | void;
|
|
235
|
+
protected afterTrigger(changes: Record<string, unknown>): Promise<void> | void;
|
|
236
236
|
prepareSerialization(): Promise<void> | void;
|
|
237
237
|
discard(): Promise<void>;
|
|
238
238
|
private updateLocalQueryCache;
|
|
@@ -240,7 +240,7 @@ declare class DataBlock {
|
|
|
240
240
|
cascade?: boolean;
|
|
241
241
|
reason?: string;
|
|
242
242
|
}): Promise<undefined>;
|
|
243
|
-
|
|
243
|
+
getChanges(state?: Record<string, unknown>): Record<string, unknown>;
|
|
244
244
|
toObject(): Record<string, unknown>;
|
|
245
245
|
toString(): string;
|
|
246
246
|
clone(options?: {
|
|
@@ -642,8 +642,7 @@ export declare class ProjectUser extends DataBlock {
|
|
|
642
642
|
get language(): string | undefined;
|
|
643
643
|
get fullName(): string | undefined;
|
|
644
644
|
get type(): "team-member" | "study-participant";
|
|
645
|
-
|
|
646
|
-
getComboboxEntry(subjects?: SubjectData[], options?: SubjectKeyOptions): {
|
|
645
|
+
getComboboxEntry(): {
|
|
647
646
|
value: string;
|
|
648
647
|
content: string;
|
|
649
648
|
};
|
|
@@ -800,6 +799,10 @@ export declare class SubjectData extends DataBlock {
|
|
|
800
799
|
static fetchSubjectKeyOptions(projectUUID?: string | undefined): Promise<SubjectKeyOptions>;
|
|
801
800
|
editableBy(user?: ProjectUser, action?: string): boolean | BlockReference<Location$1> | undefined;
|
|
802
801
|
viewableBy(user?: ProjectUser): boolean | BlockReference<Location$1> | undefined;
|
|
802
|
+
getComboboxEntry(options: SubjectKeyOptions): {
|
|
803
|
+
value: string;
|
|
804
|
+
content: string;
|
|
805
|
+
};
|
|
803
806
|
}
|
|
804
807
|
export declare class CalendarEvent extends DataBlock {
|
|
805
808
|
owner: BlockReference<User>;
|
|
@@ -871,7 +874,7 @@ export declare class Signature extends DataBlock {
|
|
|
871
874
|
status: typeof SignatureStatus[keyof typeof SignatureStatus];
|
|
872
875
|
lastUpdateDatetime: number;
|
|
873
876
|
constructor(signee?: BlockReference<User>, form?: BlockReference<FormStatus>, dataHash?: string);
|
|
874
|
-
beforeTrigger(): void;
|
|
877
|
+
beforeTrigger(changes: Record<string, unknown>): void;
|
|
875
878
|
}
|
|
876
879
|
export declare class SurveyInstance extends DataBlock {
|
|
877
880
|
id: string;
|
|
@@ -1103,12 +1106,14 @@ declare class ShareableBlock extends DataBlock {
|
|
|
1103
1106
|
sharedWithParticipants?: boolean;
|
|
1104
1107
|
locationsFilter?: BlockReference<Location$1>[];
|
|
1105
1108
|
usersFilter?: BlockReference<User>[];
|
|
1109
|
+
subjectsFilter?: BlockReference<SubjectData>[];
|
|
1106
1110
|
collaborators?: BlockReference<User>[];
|
|
1107
1111
|
constructor(owner: BlockReference<User>);
|
|
1108
|
-
get explicitUsers(): BlockReference<User>[];
|
|
1109
1112
|
userHasAccess(user?: ProjectUser, subjects?: SubjectData[]): boolean | undefined;
|
|
1110
1113
|
userMayEdit(user?: ProjectUser): boolean | undefined;
|
|
1111
|
-
usersLocationFilter(user?: ProjectUser
|
|
1114
|
+
usersLocationFilter(user?: ProjectUser): boolean | undefined;
|
|
1115
|
+
subjectsLocationFilter(subject?: SubjectData): boolean;
|
|
1116
|
+
static getWith(owner: User, recipient: BlockReference<User> | BlockReference<SubjectData>): Promise<ShareableBlock>;
|
|
1112
1117
|
}
|
|
1113
1118
|
export declare class MessengerChannel extends ShareableBlock {
|
|
1114
1119
|
title?: string;
|
|
@@ -1118,7 +1123,8 @@ export declare class ChannelMessage extends DataBlock {
|
|
|
1118
1123
|
channel: BlockReference<MessengerChannel>;
|
|
1119
1124
|
author: BlockReference<User>;
|
|
1120
1125
|
text: string;
|
|
1121
|
-
|
|
1126
|
+
participant?: BlockReference<SubjectData>;
|
|
1127
|
+
constructor(channel: BlockReference<MessengerChannel>, author: BlockReference<User>, text: string, participant?: BlockReference<SubjectData>);
|
|
1122
1128
|
}
|
|
1123
1129
|
export declare class ChannelStatus extends DataBlock {
|
|
1124
1130
|
channel: BlockReference<MessengerChannel>;
|