rxome-generator 0.1.5 → 1.0.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.TODO ADDED
@@ -0,0 +1,4 @@
1
+ ## Testing
2
+ Codes generated by the test API (i.e., with the `-t` switch or `API=Coder.TESTAPI`) can be decoded online using the following tool:
3
+
4
+ Todo: URL Code-Reader Demo
package/README.md CHANGED
@@ -5,7 +5,7 @@ Generates QR codes containing medical information for use with the FindMe2Care d
5
5
 
6
6
  ## LICENSE
7
7
 
8
- Copyright (c) 2022 MGZ-Tech GmbH
8
+ Copyright (c) 2023 RxOME GmbH
9
9
 
10
10
  All rights reserved, unauthorized use prohibited.
11
11
 
@@ -67,7 +67,8 @@ The following two async library functions generate QR codes:
67
67
 
68
68
  This function creates the QR code from the given data and writes it as PNG file specified by *filename*.
69
69
  The credentials for accessing the RxOME API (i.e., fetching a pseudonym and the encryption key) have to be
70
- part of the data object (see below). Returns the pseudonym used to generate the QR code.
70
+ part of the data object (see below). Returns the pseudonym used to generate the QR code and the unencrypted
71
+ content of the QR code.
71
72
 
72
73
  > `Coder.makeQR( data, api = RXAPI, apiEntry = APIENTRY )`
73
74
 
@@ -76,7 +77,9 @@ Generates a QR code object as Data URL that can be placed on a web page. As abov
76
77
  ```
77
78
  {
78
79
  qr_code: (QR code),
79
- pseudonym: (pseudonym used to generate the QR code)
80
+ pseudonym: (pseudonym used to generate the QR code),
81
+ qr_data: content of the QR code (with encrypted medical data; i.e., a 1:1 image of the QR code content),
82
+ qr_content: content of the QR code but with unencrypted medical data for documentation purposes
80
83
  }
81
84
  ```
82
85
 
@@ -239,13 +242,12 @@ are removed. On top level, the following section will be passed over to the QR c
239
242
  ### Overview
240
243
 
241
244
  ```
242
- RxOME.net QR Code generation tool
245
+ FindMe2care QR Code generation tool
243
246
 
244
247
  Usage: rxcode [options] [command]
245
248
 
246
249
  Basic usage: rxcode g <input json file>: generates QR Code with the basefilename of the inputfile.
247
- Before first use, please generate an API access key (rxcode -k) and deposit the public key
248
- on the RxOME server.
250
+ Before first use, please generate an API access key (rxcode -k) and deposit the public key on the FindMe2care server.
249
251
 
250
252
 
251
253
  Options:
@@ -254,20 +256,22 @@ Options:
254
256
 
255
257
  Commands:
256
258
  generate|g [options] [input file] generate QR Code from PhenoPacket JSON
257
- convert|c [options] [input file] convert case style of keys in JSON files from snake_case
258
- to camelCase (and vice versa)
259
+ convert|c [options] [input file] convert case style of keys in JSON files from snake_case to camelCase
260
+ (and vice versa)
259
261
  preprocess|p [options] [input file] perform preprocessing steps
260
262
  verify|v [input file] verify input file against phenopacket schema
261
263
  apikeys|k [options] [file prefix] generate key pair for API access
262
264
  ping|P [options] <id> <key> Ping API/check API credentials
263
- encode|e [options] [input file] encrypt medical payload (just for testing)
264
- decode|d [options] [input file] decode coded message
265
+ encrypt|e [options] [input file] encrypt message (just for testing)
266
+ decrypt|d [options] [input file] decrypt coded message or medical data
267
+ data-keys|K [options] [file prefix] generate key pair for data encryption (see -e, -d; just for testing)
265
268
  pheno2proto|E [options] [input file] encode PhenoPacket to protobuf (just for testing)
266
269
  proto2pheno|D [options] [input file] decode protobuf to PhenoPacket (just for testing)
270
+ settings|S [options] Print current settings
267
271
  statistics|s [input file] print memory consuption for several stages and alternatives
268
- rxome-keys [options] [file prefix] generate key pair for qr code data encryption
269
- (just for testing)
270
272
  help [command] display help for command
273
+
274
+ Author: Tom Kamphans, GeneTalk GmbH, 2022, (c) 2023 RxOME GmbH
271
275
  ```
272
276
 
273
277
  ### Generating QR codes
@@ -275,7 +279,7 @@ Commands:
275
279
  Use the 'g' command for actually generating a QR code:
276
280
 
277
281
  ```
278
- RxOME.net QR Code generation tool
282
+ FindMe2care QR Code generation tool
279
283
 
280
284
  Usage: rxcode generate|g [options] [input file]
281
285
 
@@ -285,27 +289,32 @@ The command line arguments precede the data from the JSON input file.
285
289
  Output: prints the given or new pseudonym.
286
290
 
287
291
  Arguments:
288
- input file Input JSON file (default: STDIN)
292
+ input file Input JSON file (default: STDIN)
289
293
 
290
294
  Options:
291
- -o, --output <filename> Filename for the QR code (default: <inputfile>.png)
292
- -p, --pseudonym Pseudonym for patient, if known. Otherwise a new is generated
293
- -i, --keyId <id> API access ID (default: input file, credentials.keyId
294
- or metaData.createdBy)
295
- -k, --keyFile <filename> Filename with API access key (default: use -s)
296
- -s, --key <key string> API access key (default: input file, credentials.key)
297
- -u, --user <user string> API access user (default: credentials.user
298
- or metaData.submittedBy or info@rxome.net)
299
- -c, --created <date> Date (default: input file, metaData.created)
300
- -l, --lab <lab> Laboratory name (default: input file, metaData.createdBy or lab name stored in the user account)
301
- -e, --email <email> Laboratory email (default: input file, metaData.submittedBy)
302
- -S, --snake Read payload formatted in snake_case (default: camelCase)
303
- -t, --test Use test API instead of production API
304
- -D, --debug Some output for debugging
305
- -h, --help display help for command
295
+ -o, --output <filename> Filename for the QR code (default: <inputfile>.png)
296
+ -p, --pseudonym <pseudonym> For re-evaluations: pseudonym for patient. Otherwise a new is generated
297
+ (default: "")
298
+ -i, --keyId <id> API access ID (default: input file, credentials.keyId or metaData.createdBy)
299
+ -k, --keyFile <filename> Filename with API access key (default: use -s)
300
+ -s, --key <key string> API access key (default: input file, credentials.key)
301
+ -u, --user <user string> API access user (default: credentials.user or metaData.submittedBy or
302
+ info@rxome.net)
303
+ -c, --created <date> Date (default: input file, metaData.created)
304
+ -l, --lab <lab> Laboratory name (default: input file, metaData.createdBy or lab name stored in
305
+ the user account)
306
+ -e, --email <email> Laboratory email (default: input file, metaData.submittedBy)
307
+ -S, --snake Read payload formatted in snake_case (default: camelCase)
308
+ -t, --test Use test API instead of production API
309
+ -L, --localhost Connect to localhost API
310
+ -D, --debug Some output for debugging
311
+ -h, --help display help for command
312
+
313
+ Author: Tom Kamphans, GeneTalk GmbH, 2022, (c) 2023 RxOME GmbH
306
314
  ```
307
315
 
308
- Writes the pseudonym used to generate the QR code to STDOUT.
316
+ Writes the pseudonym used to generate the QR code to STDOUT. With -D given, this further writes the
317
+ (unencrypted) content of the QR code to STDOUT.
309
318
 
310
319
  ### Generating API Access Keys
311
320
  To communicate with the server API you need access credentials, that is, an id for your lab (the keyId) and a pair of corresponding keys. First, generate a pair of keys with
@@ -316,7 +325,7 @@ rxcode k myLabId
316
325
 
317
326
  This yields two files: `myLabId.private.apikey` and `myLabId.public.apikey`. Store the
318
327
  private key safely.
319
- Create a lab account on `www.rxome.net/lab` and upload the public key to your profile.
328
+ Create a lab account on `app.findme2care.de/generate` and upload the public key to your profile.
320
329
  Afterwards, you should be able to access the API (see 'debugging' below).
321
330
 
322
331
  ## Demo
@@ -327,11 +336,6 @@ rxcode g -t -o qrcode.png demos/demo_data_full.json
327
336
 
328
337
  <img src="qrcode.png" width="400">
329
338
 
330
- ## Testing
331
- Codes generated by the test API (i.e., with the `-t` switch or `API=Coder.TESTAPI`) can be decoded online using the following tool:
332
-
333
- Todo: URL Code-Reader Demo
334
-
335
339
  ## Debugging
336
340
  To check the connection to the API on RxOME server API use
337
341
 
@@ -0,0 +1,145 @@
1
+ {
2
+ "id": "QR-Code ID",
3
+ "comment": "useful remarks De do do do de da da da",
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
+ "pseudonym": "_DEMO_PSEUDONYM_"
144
+ }
145
+ }
package/lib/rxome-api.cjs CHANGED
@@ -58,12 +58,20 @@ exports.unpack = unpack;
58
58
  return FS.readFileSync( name ).slice(0,44);
59
59
  }
60
60
 
61
- exports.generateApiKeys = async (name = 'rxome', dir = '.') => {
61
+ exports.generateApiKeys = async () => {
62
62
  const privateKey = ED.utils.randomPrivateKey();
63
63
  const publicKey = await ED.getPublicKey(privateKey);
64
+ return {
65
+ privateKey: privateKey,
66
+ publicKey: publicKey
67
+ }
68
+ }
69
+
70
+ exports.writeApiKeys = async (name = 'rxome', dir = '.') => {
71
+ const key = await this.generateApiKeys();
64
72
  await Promise.all([
65
- FS.writeFile( `${dir}/${name}.private.apikey`, bufferToBase64( privateKey ), { mode: 0o600 }, err => { if (err) throw err; } ),
66
- FS.writeFile( `${dir}/${name}.public.apikey`, bufferToBase64( publicKey ), { mode: 0o600 }, err => { if (err) throw err; } )
73
+ FS.writeFile( `${dir}/${name}.private.apikey`, bufferToBase64( key.privateKey ), { mode: 0o600 }, err => { if (err) throw err; } ),
74
+ FS.writeFile( `${dir}/${name}.public.apikey`, bufferToBase64( key.publicKey ), { mode: 0o600 }, err => { if (err) throw err; } )
67
75
  ]);
68
76
  }
69
77
 
@@ -4,7 +4,7 @@ const ED = require( 'noble-ed25519' );
4
4
 
5
5
  describe('API access', () => {
6
6
  test.skip('generates valid API access keys', async () => {
7
- await RxAPI.generateApiKeys( '__TESTSUITE_jesttest' );
7
+ await RxAPI.writeApiKeys( '__TESTSUITE_jesttest' );
8
8
  expect( FS.existsSync('__TESTSUITE_jesttest.private.apikey') ).toBe( true );
9
9
  expect( FS.existsSync('__TESTSUITE_jesttest.public.apikey') ).toBe( true );
10
10
  expect( FS.statSync('__TESTSUITE_jesttest.private.apikey').size - 44 ).toBeLessThan( 2 );
@@ -52,11 +52,14 @@ try {
52
52
  }
53
53
 
54
54
  exports.writeQR = async ( filename, data, api = RXAPI, apiEntry = APIENTRY ) => {
55
- const {qrData, pseudonym} = await exports.prepareQR( data, api, apiEntry );
55
+ const {qrData, pseudonym, content} = await exports.prepareQR( data, api, apiEntry );
56
56
  //const base64Data = qr_code.replace(/^data:image\/png;base64,/, "");
57
57
  //FS.writeFile(filename, base64Data, 'base64', (err) => {console.log(err)} );
58
58
  QRCode.toFile( filename, JSON.stringify( qrData ), { type: 'png'} )
59
- return pseudonym
59
+ return {
60
+ pseudonym: pseudonym,
61
+ qr_content: content
62
+ }
60
63
  }
61
64
  }
62
65
 
@@ -291,10 +294,10 @@ exports.prepareQR = async ( data, api = RXAPI, apiEntry = APIENTRY ) => {
291
294
  const key = await this.fetchKey( credentials, metaData.pseudonym || '', api, false, apiEntry );
292
295
 
293
296
  // check:
294
- //const buff = RxAPI.base64ToBuffer( base64Medical );
295
- //const pheno = exports.decodePhenoPacket( buff );
296
- //const medicalDeciphered = JSON.parse( JSON.stringify( pheno ));
297
- //console.log( JSON.stringify(medicalDeciphered,' ', 2 ))
297
+ const buff = RxAPI.base64ToBuffer( base64Medical );
298
+ const pheno = exports.decodePhenoPacket( buff );
299
+ const medicalDeciphered = JSON.parse( JSON.stringify( pheno ));
300
+ // console.log( JSON.stringify(medicalDeciphered,' ', 2 ))
298
301
 
299
302
  const cipher = await exports.encode( key.key, base64Medical );
300
303
 
@@ -309,24 +312,32 @@ exports.prepareQR = async ( data, api = RXAPI, apiEntry = APIENTRY ) => {
309
312
  labid: key.lab_id,
310
313
  keyver: key.version,
311
314
  apiver: apiVer,
312
- pseudonym: key.pseudonym,
315
+ pseudonym: key.pseudonym,
313
316
  payload: cipher.toString()
314
317
  }
315
- console.log( "[QR Generator lib ]", qrData, "\nLength: ", JSON.stringify( qrData ).length );
318
+
319
+ const qrContent = {
320
+ ...qrData,
321
+ payload: medicalDeciphered
322
+ }
323
+ // console.log( "[QR Generator lib ]", qrData, "\nLength: ", JSON.stringify( qrData ).length );
324
+ // console.log( "[QR Generator lib ]", JSON.stringify(qrContent, 0, 2));
316
325
 
317
326
  return {
318
327
  qrData: qrData,
319
- pseudonym: key.pseudonym
328
+ pseudonym: key.pseudonym,
329
+ content: qrContent
320
330
  }
321
331
  }
322
332
 
323
333
 
324
334
  exports.makeQR = async ( data, api = RXAPI, apiEntry = APIENTRY ) => {
325
- const {qrData, pseudonym} = await exports.prepareQR( data, api, apiEntry );
335
+ const {qrData, pseudonym, content} = await exports.prepareQR( data, api, apiEntry );
326
336
  return {
327
337
  qr_code: await QRCode.toDataURL( JSON.stringify( qrData )),
328
338
  pseudonym: pseudonym,
329
- qr_data: qrData
339
+ qr_data: qrData,
340
+ qr_content: content
330
341
  }
331
342
  }
332
343
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rxome-generator",
3
- "version": "0.1.5",
4
- "description": "Generates QR codes containing medical information for use with RxOME database.",
3
+ "version": "1.0.2",
4
+ "description": "Generates QR codes containing medical information for use with the FindMe2Care platform.",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "scripts": {
@@ -18,6 +18,7 @@
18
18
  },
19
19
  "keywords": [
20
20
  "RxOME",
21
+ "FineMe2care",
21
22
  "genetics",
22
23
  "variants",
23
24
  "qr-codes",
package/qrcode.png ADDED
Binary file
package/rxcode.js CHANGED
@@ -25,16 +25,17 @@ const DEMO_CREDENTIALS = ApiDemo.DEMO_CREDENTIALS
25
25
  const DEMO_PRIVATE_KEY = ApiDemo.CRYPT_PRIVATE_KEY
26
26
  const DEMO_PUBLIC_KEY = ApiDemo.CRYPT_PUBLIC_KEY
27
27
 
28
+ const VERSION = '1.0.2'
28
29
 
29
30
  program
30
31
  .name('rxcode')
31
32
  .description(
32
33
  `Basic usage: rxcode g <input json file>: generates QR Code with the basefilename of the inputfile.
33
- Before first use, please generate an API access key (rxcode -k) and deposit the public key on the RxOME server.
34
+ Before first use, please generate an API access key (rxcode -k) and deposit the public key on the FindMe2care server.
34
35
  `)
35
- .version('1.0.0')
36
- .addHelpText('beforeAll', 'RxOME.net QR Code generation tool\n')
37
- .addHelpText('afterAll', '\nAuthor: Tom Kamphans, GeneTalk GmbH, 2022');
36
+ .version( VERSION )
37
+ .addHelpText('beforeAll', 'FindMe2care QR Code generation tool\n')
38
+ .addHelpText('afterAll', '\nAuthor: Tom Kamphans, GeneTalk GmbH, 2022, (c) 2023 RxOME GmbH');
38
39
 
39
40
 
40
41
  program.command('generate')
@@ -47,7 +48,7 @@ The command line arguments precede the data from the JSON input file.
47
48
  Output: prints the given or new pseudonym.`)
48
49
  .argument('[input file]', 'Input JSON file (default: STDIN)')
49
50
  .option('-o, --output <filename>', 'Filename for the QR code (default: <inputfile>.png)')
50
- .option('-p, --pseudonym', 'vPseudonym for patient, if known. Otherwise a new is generated', '')
51
+ .option('-p, --pseudonym <pseudonym>', 'For re-evaluations: pseudonym for patient. Otherwise a new is generated', '')
51
52
  .option('-i, --keyId <id>', 'API access ID (default: input file, credentials.keyId or metaData.createdBy)')
52
53
  .option('-k, --keyFile <filename>', 'Filename with API access key (default: use -s)')
53
54
  .option('-s, --key <key string>', 'API access key (default: input file, credentials.key)')
@@ -93,11 +94,12 @@ Output: prints the given or new pseudonym.`)
93
94
  return 1;
94
95
  }
95
96
 
96
- options.debug && console.log( "Data ", qrData );
97
+ // options.debug && console.log( "Data ", qrData );
97
98
 
98
99
  const outputfile = options.output || `${Path.basename((inputfile || 'qrcode.json'), '.json')}.png`
99
- const psLab = await Coder.writeQR( outputfile, qrData, qrApi );
100
- console.log( psLab );
100
+ const data = await Coder.writeQR( outputfile, qrData, qrApi );
101
+ console.log( data.pseudonym );
102
+ options.debug && console.log( JSON.stringify( data.qr_content, 0, 2) );
101
103
  });
102
104
 
103
105
 
@@ -160,11 +162,11 @@ program.command('verify')
160
162
  program.command('apikeys')
161
163
  .summary('generate key pair for API access')
162
164
  .alias('k')
163
- .description('Generate key pair. A pair of these keys is necessary to communicate with the rxome API. Keep the private key and deposit the public key on the RxOme server.')
165
+ .description('Generate key pair. A pair of these keys is necessary to communicate with the FindMe2care API. Keep the private key and deposit the public key on the FindMe2care server.')
164
166
  .argument('[file prefix]', 'Prefix for file names (default: rxome)')
165
167
  .option('-d, --directory <dir>', 'output directory', '.')
166
168
  .action( (prefix, options) => {
167
- RxAPI.generateApiKeys( prefix || 'rxome', options.directory )
169
+ RxAPI.writeApiKeys( prefix || 'rxome', options.directory )
168
170
  });
169
171
 
170
172
 
@@ -330,6 +332,18 @@ program.command('proto2pheno')
330
332
  });
331
333
 
332
334
 
335
+ program.command('settings')
336
+ .alias('S')
337
+ .description('Print current settings')
338
+ .option('-t, --test', 'Connect to test API')
339
+ .action( (options) => {
340
+
341
+ console.log('This RxOME/FindMe2care QR generator V', VERSION );
342
+ console.log('Connecting to', options.test ? RxAPI.TESTAPI : RxAPI.API );
343
+ console.log('API', options.test ? RxAPI.APIENTRY : RxAPI.APIENTRY );
344
+
345
+ });
346
+
333
347
  program.command('statistics')
334
348
  .alias('s')
335
349
  .argument('[input file]', 'Input JSON file (default: ./demos/demo_data_full.json)')
@@ -392,7 +406,7 @@ program.command('statistics')
392
406
  // payload: cipherBin.toString()
393
407
  // }
394
408
  // console.log( "QR-Data: ", JSON.stringify( qrData ).length )
395
- //const ps_lab = await Coder.writeQR( "stat.png", qrData, RxAPI.TESTAPI );
409
+ //const { pseudonym, qr_content } = await Coder.writeQR( "stat.png", qrData, RxAPI.TESTAPI );
396
410
 
397
411
 
398
412
  console.log("===================================================================");
@@ -410,7 +424,7 @@ program.command('statistics')
410
424
  // // const fileName = options.input || './demos/demo_data_full.json';
411
425
  // // //file = inputfile || '/dev/stdin'
412
426
  // // const data = JSON.parse( FS.readFileSync( fileName ));
413
- // // // const ps_lab = await Coder.writeQR( "ZZZtest.png", data, RxAPI.TESTAPI );
427
+ // // // const { pseudonym, qr_content } = await Coder.writeQR( "ZZZtest.png", data, RxAPI.TESTAPI );
414
428
  // // console.log( data );
415
429
  // // console.log( Coder.whiteListPhenoPacket( data ) );
416
430
  // // console.log( {