rxome-generator 1.0.4-beta.1 → 1.0.4-beta.3
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/JSON_INPUT_FORMAT.md +380 -0
- package/demos/demo_overfull.json +144 -0
- package/lib/phenopackets.js +2 -1
- package/lib/rxome-api.js +10 -0
- package/lib/rxome-generator.js +6 -2
- package/lib/rxome-generator.test.js +2 -0
- package/package.json +3 -2
- package/rxcode.js +4 -0
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
# RxOME QR Code Generator -- JSON Input Format
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The RxOME QR code generator accepts a JSON object following the
|
|
6
|
+
**PhenoPacket v2** standard with RxOME-specific extensions. The input
|
|
7
|
+
describes a patient's clinical and genomic data, which is then
|
|
8
|
+
protobuf-encoded, encrypted, and rendered as a QR code.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Top-Level Structure
|
|
13
|
+
|
|
14
|
+
```json
|
|
15
|
+
{
|
|
16
|
+
"id": "string",
|
|
17
|
+
"comment": "string",
|
|
18
|
+
"subject": { ... },
|
|
19
|
+
"phenotypicFeatures": [ ... ],
|
|
20
|
+
"compressedFeatures": { ... },
|
|
21
|
+
"interpretations": [ ... ],
|
|
22
|
+
"diagnosis": { ... },
|
|
23
|
+
"metaData": { ... },
|
|
24
|
+
"credentials": { ... }
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
All top-level fields are optional except `credentials` (required for QR
|
|
29
|
+
generation but stripped before encoding).
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Field Reference
|
|
34
|
+
|
|
35
|
+
### Root Fields
|
|
36
|
+
|
|
37
|
+
| Field | Type | Required | Description |
|
|
38
|
+
|-------|------|----------|-------------|
|
|
39
|
+
| `id` | string | No | Unique QR code / record identifier |
|
|
40
|
+
| `comment` | string | No | Free-text remarks |
|
|
41
|
+
| `subject` | object | No | Patient demographics |
|
|
42
|
+
| `phenotypicFeatures` | array | No | HPO terms (detailed form) |
|
|
43
|
+
| `compressedFeatures` | object | No | HPO terms (compact form, preferred) |
|
|
44
|
+
| `interpretations` | array | No | Genomic findings and diagnoses |
|
|
45
|
+
| `diagnosis` | object | No | Disease/condition information |
|
|
46
|
+
| `metaData` | object | No | Creator info, timestamps, schema version |
|
|
47
|
+
| `credentials` | object | **Yes** | API credentials (never encoded in QR) |
|
|
48
|
+
|
|
49
|
+
> **Note:** Supply either `phenotypicFeatures` or `compressedFeatures`,
|
|
50
|
+
> not both. If `phenotypicFeatures` is provided it is automatically
|
|
51
|
+
> converted to `compressedFeatures` during preprocessing.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
### `subject` -- Patient Information
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
"subject": {
|
|
59
|
+
"id": "proband A",
|
|
60
|
+
"dateOfBirth": "1994-01-01T00:00:00Z",
|
|
61
|
+
"sex": "FEMALE",
|
|
62
|
+
"gender": { "id": "...", "label": "..." },
|
|
63
|
+
"alternateIds": ["ID-2"],
|
|
64
|
+
"vitalStatus": { ... },
|
|
65
|
+
"karyotypicSex": "XX",
|
|
66
|
+
"taxonomy": { "id": "NCBITaxon:9606", "label": "Homo sapiens" },
|
|
67
|
+
"timeAtLastEncounter": { ... }
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Sex values:** `"UNKNOWN_SEX"` / `0`, `"FEMALE"` / `1`, `"MALE"` / `2`,
|
|
72
|
+
`"OTHER_SEX"` / `3`. String values are converted to numbers during
|
|
73
|
+
sanitisation.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
### `phenotypicFeatures` -- Detailed HPO Terms
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
"phenotypicFeatures": [
|
|
81
|
+
{
|
|
82
|
+
"type": { "id": "HP:0030084", "label": "Clinodactyly" },
|
|
83
|
+
"excluded": false,
|
|
84
|
+
"severity": { "id": "HP:0012825", "label": "Mild" },
|
|
85
|
+
"modifiers": [],
|
|
86
|
+
"onset": { ... },
|
|
87
|
+
"resolution": { ... },
|
|
88
|
+
"evidence": [],
|
|
89
|
+
"description": "..."
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Set `excluded: true` to indicate a feature is explicitly absent.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
### `compressedFeatures` -- Compact HPO Terms (Preferred)
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
"compressedFeatures": {
|
|
102
|
+
"includes": ["HP:0030084", "HP:0000555", "HP:0000486"],
|
|
103
|
+
"excludes": ["HP:0031360"]
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
This is the preferred input form. It contains only the HPO IDs, separated
|
|
108
|
+
into present (`includes`) and absent (`excludes`) features.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
### `interpretations` -- Genomic Findings
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
"interpretations": [
|
|
116
|
+
{
|
|
117
|
+
"id": "interpretation.id",
|
|
118
|
+
"progressStatus": "SOLVED",
|
|
119
|
+
"diagnosis": {
|
|
120
|
+
"disease": {
|
|
121
|
+
"id": "OMIM:263750",
|
|
122
|
+
"label": "Optional disease label"
|
|
123
|
+
},
|
|
124
|
+
"genomicInterpretations": [
|
|
125
|
+
{
|
|
126
|
+
"subjectOrBiosampleId": "optional.id",
|
|
127
|
+
"interpretationStatus": 0,
|
|
128
|
+
"variantInterpretation": {
|
|
129
|
+
"acmgPathogenicityClassification": "PATHOGENIC",
|
|
130
|
+
"variationDescriptor": {
|
|
131
|
+
"geneContext": {
|
|
132
|
+
"valueId": "HGNC:9884",
|
|
133
|
+
"symbol": "RB1",
|
|
134
|
+
"alternateIds": ["5925"],
|
|
135
|
+
"description": "..."
|
|
136
|
+
},
|
|
137
|
+
"expressions": [
|
|
138
|
+
{ "syntax": "hgvs.c", "value": "NM_000321.2:c.958C>T" },
|
|
139
|
+
{ "syntax": "iscn", "value": "..." }
|
|
140
|
+
],
|
|
141
|
+
"allelicState": {
|
|
142
|
+
"id": "GENO:0000135",
|
|
143
|
+
"label": "heterozygous"
|
|
144
|
+
},
|
|
145
|
+
"extensions": [
|
|
146
|
+
{ "name": "test-type", "value": "Exome, short read" },
|
|
147
|
+
{ "name": "cnv", "value": "1" },
|
|
148
|
+
{ "name": "meth", "value": "1" },
|
|
149
|
+
{ "name": "af", "value": "0.5" },
|
|
150
|
+
{ "name": "rl", "value": "42" },
|
|
151
|
+
{ "name": "chr", "value": "13q14.2" },
|
|
152
|
+
{ "name": "site", "value": "..." },
|
|
153
|
+
{ "name": "upd", "value": "..." }
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
]
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
#### Enum Values
|
|
165
|
+
|
|
166
|
+
**progressStatus:**
|
|
167
|
+
`"UNKNOWN_PROGRESS"` (0), `"IN_PROGRESS"` (1), `"COMPLETED"` (2),
|
|
168
|
+
`"SOLVED"` (3), `"UNSOLVED"` (4)
|
|
169
|
+
|
|
170
|
+
**acmgPathogenicityClassification:**
|
|
171
|
+
`"NOT_PROVIDED"` (0), `"BENIGN"` (1), `"LIKELY_BENIGN"` (2),
|
|
172
|
+
`"UNCERTAIN_SIGNIFICANCE"` (3), `"LIKELY_PATHOGENIC"` (4),
|
|
173
|
+
`"PATHOGENIC"` (5), `"RISK_ALLELE"` (999)
|
|
174
|
+
|
|
175
|
+
> **Note that `RISK_ALLELE` is a RxOME specific extension of the PhenoPackage standard.**
|
|
176
|
+
|
|
177
|
+
**allelicState (Zygosity, GENO ontology):**
|
|
178
|
+
|
|
179
|
+
| Code | Meaning |
|
|
180
|
+
|------|---------|
|
|
181
|
+
| `GENO_0000137` | Unspecified zygosity |
|
|
182
|
+
| `GENO_0000136` | Homozygous |
|
|
183
|
+
| `GENO_0000135` | Heterozygous |
|
|
184
|
+
| `GENO_0000402` | Compound heterozygous |
|
|
185
|
+
| `GENO_0000134` | Hemizygous |
|
|
186
|
+
| `GENO_0000604` | Hemizygous X-linked |
|
|
187
|
+
| `GENO_0000605` | Hemizygous Y-linked |
|
|
188
|
+
| `GENO_0000606` | Hemizygous insertion-linked |
|
|
189
|
+
| `GENO_0000392` | Aneusomic zygosity |
|
|
190
|
+
| `GENO_0000393` | Trisomic homozygous |
|
|
191
|
+
| `GENO_0000394` | Trisomic heterozygous |
|
|
192
|
+
| `GENO_0000602` | Homoplasmic |
|
|
193
|
+
| `GENO_0000603` | Heteroplasmic |
|
|
194
|
+
| `GENO_0000964` | Mosaic |
|
|
195
|
+
|
|
196
|
+
#### RxOME Extension Fields
|
|
197
|
+
|
|
198
|
+
These are carried in the `extensions` array of a `variationDescriptor`:
|
|
199
|
+
|
|
200
|
+
| Name | Values | Description |
|
|
201
|
+
|------|--------|-------------|
|
|
202
|
+
| `test-type` | Free text (e.g. "Exome, short read", "Multigene panel") | Genetic test performed |
|
|
203
|
+
| `cnv` | `0` = not provided, `1` = deletion, `2` = duplication | Copy-number variation |
|
|
204
|
+
| `meth` | `0` = not provided, `1` = hyper, `2` = hypo, `3` = intermediate | Methylation status |
|
|
205
|
+
| `af` | Numeric string | Allele frequency |
|
|
206
|
+
| `rl` | Numeric string | Repeat length |
|
|
207
|
+
| `chr` | String (e.g. "13q14.2") | Chromosomal region |
|
|
208
|
+
| `site` | String | Methylation site |
|
|
209
|
+
| `upd` | String | Uniparental disomy |
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
### `metaData`
|
|
214
|
+
|
|
215
|
+
```json
|
|
216
|
+
"metaData": {
|
|
217
|
+
"created": "2021-05-14T10:35:00Z",
|
|
218
|
+
"createdBy": "MGZ Munich",
|
|
219
|
+
"submittedBy": "clinician@mgz-muenchen.de",
|
|
220
|
+
"pseudonym": "WDJ6GW2MMZ6A",
|
|
221
|
+
"phenopacketSchemaVersion": "2.0",
|
|
222
|
+
"resources": [],
|
|
223
|
+
"externalReferences": []
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
### `credentials` -- API Access (Never in QR Code)
|
|
230
|
+
|
|
231
|
+
```json
|
|
232
|
+
"credentials": {
|
|
233
|
+
"keyId": "rxome",
|
|
234
|
+
"key": "Rf7VbeUBQmjvAagwsWx6riaZYc7h4OBD4CuxYyZ5bgA=",
|
|
235
|
+
"keyFile": "/path/to/key",
|
|
236
|
+
"user": "clinician@mgz-muenchen.de"
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Provide either `key` (Base64-encoded Ed25519 private key) or `keyFile`
|
|
241
|
+
(path to key file), not both. This object is stripped before any data
|
|
242
|
+
enters the QR code.
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Encoding Pipeline: Plain vs. Protobuf-Compressed Fields
|
|
247
|
+
|
|
248
|
+
The QR code contains two distinct areas: **plain-text metadata** (the
|
|
249
|
+
outer JSON envelope) and an **encrypted payload** (protobuf-encoded
|
|
250
|
+
medical data). The pipeline is:
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
Input JSON
|
|
254
|
+
|
|
|
255
|
+
v
|
|
256
|
+
[1] Whitelist -- keep only recognised PhenoPacket fields; strip credentials
|
|
257
|
+
|
|
|
258
|
+
v
|
|
259
|
+
[2] Sanitise -- convert string enums to numeric (sex, progressStatus, ACMG)
|
|
260
|
+
|
|
|
261
|
+
v
|
|
262
|
+
[3] HPO compress -- convert phenotypicFeatures[] to compressedFeatures{}
|
|
263
|
+
|
|
|
264
|
+
v
|
|
265
|
+
[4] Protobuf encode -- encode entire medical object to binary (PhenoPacket v2 schema)
|
|
266
|
+
|
|
|
267
|
+
v
|
|
268
|
+
[5] Base64
|
|
269
|
+
|
|
|
270
|
+
v
|
|
271
|
+
[6] PGP encrypt (OpenPGP, Curve25519)
|
|
272
|
+
|
|
|
273
|
+
v
|
|
274
|
+
[7] Assemble QR JSON envelope
|
|
275
|
+
|
|
|
276
|
+
v
|
|
277
|
+
[8] Render QR code (PNG)
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### What Ends Up in the QR Code
|
|
281
|
+
|
|
282
|
+
The final QR code contains a JSON string with this structure:
|
|
283
|
+
|
|
284
|
+
```json
|
|
285
|
+
{
|
|
286
|
+
"createdBy": "MGZ Munich",
|
|
287
|
+
"labid": "rxome",
|
|
288
|
+
"keyver": "2",
|
|
289
|
+
"apiver": "1.0",
|
|
290
|
+
"pseudonym": "WDJ6GW2MMZ6A",
|
|
291
|
+
"payload": "<PGP-encrypted, Base64-encoded protobuf binary>"
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Field Classification: Plain-Text vs. Protobuf+Encrypted
|
|
296
|
+
|
|
297
|
+
| Field | Storage in QR | Explanation |
|
|
298
|
+
|-------|--------------|-------------|
|
|
299
|
+
| **PLAIN-TEXT (QR envelope, readable without decryption)** | |
|
|
300
|
+
| `createdBy` | plain | Lab/creator name from metaData |
|
|
301
|
+
| `submittedBy` | plain | Submitter email from metaData |
|
|
302
|
+
| `created` | plain | Creation timestamp from metaData |
|
|
303
|
+
| `labid` | plain | Lab identifier from credentials.keyId |
|
|
304
|
+
| `keyver` | plain | Encryption key version (from API) |
|
|
305
|
+
| `apiver` | plain | API version string |
|
|
306
|
+
| `pseudonym` | plain | Patient pseudonym (from API) |
|
|
307
|
+
| **PROTOBUF-ENCODED + ENCRYPTED (inside `payload`)** | |
|
|
308
|
+
| `id` | protobuf + encrypted | Record identifier |
|
|
309
|
+
| `comment` | protobuf + encrypted | Free-text remarks |
|
|
310
|
+
| `subject` | protobuf + encrypted | All patient demographics (DOB, sex, ...) |
|
|
311
|
+
| `compressedFeatures` | protobuf + encrypted | HPO phenotype terms (includes + excludes) |
|
|
312
|
+
| `interpretations` | protobuf + encrypted | All genomic findings, variants, diagnoses |
|
|
313
|
+
| `diagnosis` | protobuf + encrypted | Disease/condition information |
|
|
314
|
+
| `metaData` | protobuf + encrypted | Full metadata structure (without pseudonym) |
|
|
315
|
+
| **NEVER IN QR** | |
|
|
316
|
+
| `credentials` | not stored | Stripped before encoding; used only for API auth |
|
|
317
|
+
|
|
318
|
+
> **Key insight:** The outer QR envelope carries only non-sensitive
|
|
319
|
+
> routing metadata (who created it, pseudonym, key version). All clinical
|
|
320
|
+
> and genomic data is protobuf-serialised, Base64-encoded, then
|
|
321
|
+
> PGP-encrypted before being placed in the `payload` field.
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Complete Example
|
|
326
|
+
|
|
327
|
+
```json
|
|
328
|
+
{
|
|
329
|
+
"id": "QR-Code ID",
|
|
330
|
+
"comment": "useful remarks",
|
|
331
|
+
"subject": {
|
|
332
|
+
"id": "proband A",
|
|
333
|
+
"dateOfBirth": "1994-01-01T00:00:00Z",
|
|
334
|
+
"sex": "FEMALE"
|
|
335
|
+
},
|
|
336
|
+
"compressedFeatures": {
|
|
337
|
+
"includes": ["HP:0030084", "HP:0000555", "HP:0000486"],
|
|
338
|
+
"excludes": ["HP:0031360"]
|
|
339
|
+
},
|
|
340
|
+
"interpretations": [
|
|
341
|
+
{
|
|
342
|
+
"id": "interpretation.id",
|
|
343
|
+
"progressStatus": "SOLVED",
|
|
344
|
+
"diagnosis": {
|
|
345
|
+
"disease": { "id": "OMIM:263750" },
|
|
346
|
+
"genomicInterpretations": [
|
|
347
|
+
{
|
|
348
|
+
"variantInterpretation": {
|
|
349
|
+
"acmgPathogenicityClassification": "PATHOGENIC",
|
|
350
|
+
"variationDescriptor": {
|
|
351
|
+
"geneContext": {
|
|
352
|
+
"valueId": "HGNC:9884",
|
|
353
|
+
"symbol": "RB1"
|
|
354
|
+
},
|
|
355
|
+
"expressions": [
|
|
356
|
+
{ "syntax": "hgvs.c", "value": "NM_000321.2:c.958C>T" }
|
|
357
|
+
],
|
|
358
|
+
"allelicState": { "id": "GENO:0000135" },
|
|
359
|
+
"extensions": [
|
|
360
|
+
{ "name": "test-type", "value": "Exome, short read" }
|
|
361
|
+
]
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
]
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
],
|
|
369
|
+
"metaData": {
|
|
370
|
+
"created": "2021-05-14T10:35:00Z",
|
|
371
|
+
"createdBy": "MGZ Munich",
|
|
372
|
+
"submittedBy": "clinician@mgz-muenchen.de"
|
|
373
|
+
},
|
|
374
|
+
"credentials": {
|
|
375
|
+
"key": "Rf7VbeUBQmjvAagwsWx6riaZYc7h4OBD4CuxYyZ5bgA=",
|
|
376
|
+
"keyId": "rxome",
|
|
377
|
+
"user": "clinician@mgz-muenchen.de"
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
```
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "QR-Code ID",
|
|
3
|
+
"comment": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat. Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in",
|
|
4
|
+
"subject": {
|
|
5
|
+
"id": "proband A",
|
|
6
|
+
"dateOfBirth": "1994-01-01T00:00:00Z",
|
|
7
|
+
"sex": "FEMALE"
|
|
8
|
+
},
|
|
9
|
+
"phenotypicFeatures": [
|
|
10
|
+
{
|
|
11
|
+
"type": {
|
|
12
|
+
"id": "HP:0030084"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"type": {
|
|
17
|
+
"id": "HP:0000555"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"type": {
|
|
22
|
+
"id": "HP:0000486"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"type": {
|
|
27
|
+
"id": "HP:0000541"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"type": {
|
|
32
|
+
"id": "HP:0084369"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"type": {
|
|
37
|
+
"id": "HP:0112358"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"type": {
|
|
42
|
+
"id": "HP:0000145"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"type": {
|
|
47
|
+
"id": "HP:1234567"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"type": {
|
|
52
|
+
"id": "HP:9876543"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"type": {
|
|
57
|
+
"id": "HP:5678912"
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"type": {
|
|
62
|
+
"id": "HP:0031360"
|
|
63
|
+
},
|
|
64
|
+
"excluded": true
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"type": {
|
|
68
|
+
"id": "HP:0001234"
|
|
69
|
+
},
|
|
70
|
+
"excluded": true
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
"interpretations": [
|
|
74
|
+
{
|
|
75
|
+
"id": "interpretation.id",
|
|
76
|
+
"progressStatus": "SOLVED",
|
|
77
|
+
"diagnosis": {
|
|
78
|
+
"disease": {
|
|
79
|
+
"id": "OMIM:263750"
|
|
80
|
+
},
|
|
81
|
+
"genomicInterpretations": [
|
|
82
|
+
{
|
|
83
|
+
"variantInterpretation": {
|
|
84
|
+
"acmgPathogenicityClassification": "PATHOGENIC",
|
|
85
|
+
"variationDescriptor": {
|
|
86
|
+
"geneContext": {
|
|
87
|
+
"valueId": "HGNC:9884",
|
|
88
|
+
"symbol": "RB1"
|
|
89
|
+
},
|
|
90
|
+
"expressions": [
|
|
91
|
+
{
|
|
92
|
+
"syntax": "hgvs.c",
|
|
93
|
+
"value": "NM_000321.2:c.958C>T"
|
|
94
|
+
}
|
|
95
|
+
],
|
|
96
|
+
"allelicState": {
|
|
97
|
+
"id": "GENO:0000135"
|
|
98
|
+
},
|
|
99
|
+
"extensions": [
|
|
100
|
+
{
|
|
101
|
+
"name": "test-type",
|
|
102
|
+
"value": "Exome, short read"
|
|
103
|
+
}
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"variantInterpretation": {
|
|
110
|
+
"acmgPathogenicityClassification": "LIKELY_PATHOGENIC",
|
|
111
|
+
"variationDescriptor": {
|
|
112
|
+
"geneContext": {
|
|
113
|
+
"valueId": "HGNC:9884",
|
|
114
|
+
"symbol": "RB1"
|
|
115
|
+
},
|
|
116
|
+
"expressions": [
|
|
117
|
+
{
|
|
118
|
+
"syntax": "hgvs.c",
|
|
119
|
+
"value": "NM_000321.2:c.1234A>G"
|
|
120
|
+
}
|
|
121
|
+
],
|
|
122
|
+
"allelicState": {
|
|
123
|
+
"label": "heterozygous"
|
|
124
|
+
},
|
|
125
|
+
"extensions": [
|
|
126
|
+
{
|
|
127
|
+
"name": "test-type",
|
|
128
|
+
"value": "Exome, short read"
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
],
|
|
138
|
+
"metaData": {
|
|
139
|
+
"created": "2021-05-14T10:35:00Z",
|
|
140
|
+
"createdBy": "mgz",
|
|
141
|
+
"submittedBy": "a_clinician@mgz-muenchen.de",
|
|
142
|
+
"phenopacketSchemaVersion": "2.0"
|
|
143
|
+
}
|
|
144
|
+
}
|
package/lib/phenopackets.js
CHANGED
package/lib/rxome-api.js
CHANGED
|
@@ -3,6 +3,14 @@ import * as ED from 'noble-ed25519';
|
|
|
3
3
|
import Protobuf from 'protobufjs';
|
|
4
4
|
import { stringify } from 'querystring';
|
|
5
5
|
|
|
6
|
+
// Environment-aware fetch function
|
|
7
|
+
export async function getFetch() {
|
|
8
|
+
if (typeof window !== 'undefined') {
|
|
9
|
+
return window.fetch;
|
|
10
|
+
}
|
|
11
|
+
return (await import('node-fetch')).default;
|
|
12
|
+
}
|
|
13
|
+
|
|
6
14
|
const API = 'https://app.findme2care.de';
|
|
7
15
|
const TESTAPI = 'https://stage.findme2care.de';
|
|
8
16
|
const APIENTRY = 'api/v1';
|
|
@@ -122,6 +130,7 @@ export const fetchData = async ( url, credentials, pseudonym = '', debug = false
|
|
|
122
130
|
const controller = new AbortController();
|
|
123
131
|
const timeoutId = setTimeout(() => controller.abort(), 5000);
|
|
124
132
|
|
|
133
|
+
const fetch = await getFetch();
|
|
125
134
|
return fetch(fullUrl, {
|
|
126
135
|
method: 'GET',
|
|
127
136
|
headers: {
|
|
@@ -186,6 +195,7 @@ export const pushData = async ( url, credentials, msg, debug = false ) => {
|
|
|
186
195
|
const controller = new AbortController();
|
|
187
196
|
const timeoutId = setTimeout(() => controller.abort(), 5000);
|
|
188
197
|
|
|
198
|
+
const fetch = await getFetch();
|
|
189
199
|
return fetch(url, {
|
|
190
200
|
method: 'POST',
|
|
191
201
|
headers: {
|
package/lib/rxome-generator.js
CHANGED
|
@@ -150,11 +150,15 @@ export const sanitizeProgState = ( str ) =>
|
|
|
150
150
|
: +str;
|
|
151
151
|
|
|
152
152
|
|
|
153
|
-
export const sanitizeACMG = ( str ) =>
|
|
153
|
+
export const sanitizeACMG = ( str ) => {
|
|
154
|
+
// special case for 'RISK_ALLELE'
|
|
155
|
+
if ( str == 'RISK_ALLELE' ) {
|
|
156
|
+
return 999;
|
|
157
|
+
}
|
|
154
158
|
isNaN(+(str.toString()))
|
|
155
159
|
? Math.max(0, ENUM_ACMG.indexOf(str.toUpperCase()))
|
|
156
160
|
: +str
|
|
157
|
-
|
|
161
|
+
}
|
|
158
162
|
|
|
159
163
|
export const whiteListPhenoPacket = ( data ) => {
|
|
160
164
|
let res = {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as Coder from './rxome-generator.js';
|
|
2
2
|
import * as RxAPI from './rxome-api.js';
|
|
3
3
|
import * as ApiDemo from './rxome-api-demo.js';
|
|
4
|
+
import { getFetch } from './rxome-api.js';
|
|
4
5
|
|
|
5
6
|
import { readFileSync } from 'fs';
|
|
6
7
|
// Using native fetch API instead of axios
|
|
@@ -294,6 +295,7 @@ describe('Rails', () => {
|
|
|
294
295
|
const params = new URLSearchParams({
|
|
295
296
|
cipher: cipher
|
|
296
297
|
});
|
|
298
|
+
const fetch = await getFetch();
|
|
297
299
|
const res = await fetch(`${RXTESTAPI}/api/v1/decryptTest?${params}`, {
|
|
298
300
|
method: 'GET',
|
|
299
301
|
headers: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rxome-generator",
|
|
3
|
-
"version": "1.0.4-beta.
|
|
3
|
+
"version": "1.0.4-beta.3",
|
|
4
4
|
"description": "Generates QR codes containing medical information for use with the FindMe2Care platform.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -32,9 +32,10 @@
|
|
|
32
32
|
"homepage": "https://github.com/GeneTalkTK/rxome-qrcode-generator#readme",
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@noble/ed25519": "^2.2.3",
|
|
35
|
-
"commander": "^
|
|
35
|
+
"commander": "^9.4.0",
|
|
36
36
|
"json-key-converter": "^1.0.0",
|
|
37
37
|
"noble-ed25519": "^1.2.6",
|
|
38
|
+
"node-fetch": "^3.3.2",
|
|
38
39
|
"openpgp": "^6.1.0",
|
|
39
40
|
"protobufjs": "^7.4.0",
|
|
40
41
|
"protobufjs-cli": "^1.1.3",
|
package/rxcode.js
CHANGED
|
@@ -186,6 +186,10 @@ program.command('preprocess')
|
|
|
186
186
|
.option('-c, --compress', 'Compact HPO term list')
|
|
187
187
|
.action( async (inputfile, options) => {
|
|
188
188
|
let data = JSON.parse(FS.readFileSync( inputfile || '/dev/stdin' ));
|
|
189
|
+
if (! (options.case || options.whitelist || options.sanitize || options.compress)) {
|
|
190
|
+
process.stderr.write( 'Error: at least one preprocessing option must be selected (-C, -w, -s, -c).');
|
|
191
|
+
return 1;
|
|
192
|
+
}
|
|
189
193
|
options.case && (data = Coder.convertToCamelCase( data ));
|
|
190
194
|
options.whitelist && (data = Coder.whiteListPhenoPacket( data ));
|
|
191
195
|
options.sanitize && (data = Coder.sanitizePhenoPacket( data ));
|