@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 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. Assumed for the rest of the examples.
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
- get changedFields(): Record<string, unknown>;
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
- getMemberNameOrSubjectId(subjects?: SubjectData[], options?: SubjectKeyOptions): string | undefined;
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, subjects?: SubjectData[]): boolean | undefined;
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
- constructor(channel: BlockReference<MessengerChannel>, author: BlockReference<User>, text: string);
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>;