@opencloning/utils 2.1.1 → 2.2.1

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "license": "MIT",
3
3
  "name": "@opencloning/utils",
4
- "version": "2.1.1",
4
+ "version": "2.2.1",
5
5
  "type": "module",
6
6
  "main": "./src/utils/index.js",
7
7
  "exports": {
@@ -29,14 +29,14 @@
29
29
  },
30
30
  "repository": {
31
31
  "type": "git",
32
- "url": "https://github.com/OpenCloning/OpenCloning_frontend.git",
33
- "directory": "packages/utils"
32
+ "url": "https://github.com/OpenCloning/OpenCloning.git",
33
+ "directory": "js/packages/utils"
34
34
  },
35
35
  "dependencies": {
36
- "@opencloning/store": "2.1.1",
36
+ "@opencloning/store": "2.2.1",
37
37
  "@teselagen/bio-parsers": "^0.4.38",
38
38
  "@teselagen/sequence-utils": "^0.3.42",
39
- "@zip.js/zip.js": "^2.8.26",
39
+ "@zip.js/zip.js": "^2.24.0",
40
40
  "dom-to-svg": "^0.12.2",
41
41
  "lodash-es": "^4.18.1"
42
42
  },
@@ -91,6 +91,23 @@ export function buildAlignmentConfig(id, sequenceData, alignmentTracks) {
91
91
  };
92
92
  }
93
93
 
94
+ /**
95
+ * Build the alignment config of sequencing files aligned to a sequence (response of the align_sanger endpoint).
96
+ * @param {Object} sequenceModel - The TextFileSequence that was aligned (its id names the alignment)
97
+ * @param {Object} sequenceData - The reference sequence data
98
+ * @param {Array} parsedFiles - [{ fileName, fileContent }], fileContent is getTeselaJsonFromBase64 output
99
+ * @param {Array} alignedSequences - align_sanger response: the aligned reference, then one aligned string per file
100
+ * @returns {Object} Alignment config for addAlignment
101
+ */
102
+ export function buildSequencingAlignmentConfig(sequenceModel, sequenceData, parsedFiles, alignedSequences) {
103
+ const referenceTrack = buildReferenceTrack(sequenceData, alignedSequences[0]);
104
+ const otherTracks = parsedFiles.map((f, i) => {
105
+ const alignmentFile = { file_name: f.fileName, alignment: [alignedSequences[0], alignedSequences[i + 1]] }; // eslint-disable-line camelcase
106
+ return buildAlignmentTrack(f.fileContent, alignmentFile);
107
+ });
108
+ return buildAlignmentConfig(sequenceModel.id, sequenceData, [referenceTrack, ...otherTracks]);
109
+ }
110
+
94
111
  /**
95
112
  * Remove a panel from panelsShown by id.
96
113
  * @param {Array} panelsShown - Current panelsShown array (array of arrays)
@@ -13,7 +13,7 @@ describe('delimitedFileToJson', () => {
13
13
  const file = createFile('name,sequence\nprimer1,ATGC\nprimer2,CGTG', 'test.csv');
14
14
 
15
15
  const result = await delimitedFileToJson(file);
16
-
16
+
17
17
  expect(result).toEqual([
18
18
  { name: 'primer1', sequence: 'ATGC' },
19
19
  { name: 'primer2', sequence: 'CGTG' }
@@ -24,7 +24,7 @@ describe('delimitedFileToJson', () => {
24
24
  const file = createFile('name;sequence\nprimer1;ATGC\nprimer2;CGTG', 'test.csv');
25
25
 
26
26
  const result = await delimitedFileToJson(file);
27
-
27
+
28
28
  expect(result).toEqual([
29
29
  { name: 'primer1', sequence: 'ATGC' },
30
30
  { name: 'primer2', sequence: 'CGTG' }
@@ -35,7 +35,7 @@ describe('delimitedFileToJson', () => {
35
35
  const file = createFile('name\tsequence\nprimer1\tATGC\nprimer2\tCGTG', 'test.tsv');
36
36
 
37
37
  const result = await delimitedFileToJson(file);
38
-
38
+
39
39
  expect(result).toEqual([
40
40
  { name: 'primer1', sequence: 'ATGC' },
41
41
  { name: 'primer2', sequence: 'CGTG' }
@@ -70,7 +70,7 @@ describe('delimitedFileToJson', () => {
70
70
  const file = createFile('name,sequence\n\nprimer1,ATGC\n \nprimer2,CGTG', 'test.csv');
71
71
 
72
72
  const result = await delimitedFileToJson(file);
73
-
73
+
74
74
  expect(result).toEqual([
75
75
  { name: 'primer1', sequence: 'ATGC' },
76
76
  { name: 'primer2', sequence: 'CGTG' }
@@ -107,7 +107,7 @@ describe('delimitedFileToJson', () => {
107
107
  const file = createFile('name,sequence\r\nprimer1,ATGC\r\nprimer2,CGTG', 'test.csv');
108
108
 
109
109
  const result = await delimitedFileToJson(file);
110
-
110
+
111
111
  expect(result).toEqual([
112
112
  { name: 'primer1', sequence: 'ATGC' },
113
113
  { name: 'primer2', sequence: 'CGTG' }
@@ -118,7 +118,7 @@ describe('delimitedFileToJson', () => {
118
118
  const file = createFile('name,sequence\rprimer1,ATGC\rprimer2,CGTG', 'test.csv');
119
119
 
120
120
  const result = await delimitedFileToJson(file);
121
-
121
+
122
122
  expect(result).toEqual([
123
123
  { name: 'primer1', sequence: 'ATGC' },
124
124
  { name: 'primer2', sequence: 'CGTG' }
@@ -129,7 +129,7 @@ describe('delimitedFileToJson', () => {
129
129
  const file = createFile('name,sequence\nprimer1,ATGC\nprimer2,CGTG', 'test.csv');
130
130
 
131
131
  const result = await delimitedFileToJson(file);
132
-
132
+
133
133
  expect(result).toEqual([
134
134
  { name: 'primer1', sequence: 'ATGC' },
135
135
  { name: 'primer2', sequence: 'CGTG' }
@@ -140,7 +140,7 @@ describe('delimitedFileToJson', () => {
140
140
  const file = createFile('name,sequence\nprimer1,ATGC', 'test.csv');
141
141
 
142
142
  const result = await delimitedFileToJson(file, []);
143
-
143
+
144
144
  expect(result).toEqual([
145
145
  { name: 'primer1', sequence: 'ATGC' }
146
146
  ]);
@@ -150,7 +150,7 @@ describe('delimitedFileToJson', () => {
150
150
  const file = createFile('name,sequence\nprimer1,ATGC', 'test.csv');
151
151
 
152
152
  const result = await delimitedFileToJson(file);
153
-
153
+
154
154
  expect(result).toEqual([
155
155
  { name: 'primer1', sequence: 'ATGC' }
156
156
  ]);
@@ -172,7 +172,7 @@ describe('primersFromTextFile', () => {
172
172
  const file = createFile('name,sequence\nprimer1,ATGC\nprimer2,CGTG', 'test.csv');
173
173
 
174
174
  const result = await primersFromTextFile(file, []);
175
-
175
+
176
176
  expect(result).toEqual([
177
177
  { name: 'primer1', sequence: 'ATGC', error: '' },
178
178
  { name: 'primer2', sequence: 'CGTG', error: '' }
@@ -183,7 +183,7 @@ describe('primersFromTextFile', () => {
183
183
  const file = createFile('name,sequence\nprimer1,ATGC\nprimer2,CGTG', 'test.csv');
184
184
 
185
185
  const result = await primersFromTextFile(file, ['primer1']);
186
-
186
+
187
187
  expect(result).toEqual([
188
188
  { name: 'primer1', sequence: 'ATGC', error: 'existing' },
189
189
  { name: 'primer2', sequence: 'CGTG', error: '' }
@@ -194,7 +194,7 @@ describe('primersFromTextFile', () => {
194
194
  const file = createFile('name,sequence\nprimer1,ATGC\nprimer2,XYZ', 'test.csv');
195
195
 
196
196
  const result = await primersFromTextFile(file, []);
197
-
197
+
198
198
  expect(result).toEqual([
199
199
  { name: 'primer1', sequence: 'ATGC', error: '' },
200
200
  { name: 'primer2', sequence: 'XYZ', error: 'invalid' }
@@ -205,7 +205,7 @@ describe('primersFromTextFile', () => {
205
205
  const file = createFile('name,sequence\nprimer1,XYZ', 'test.csv');
206
206
 
207
207
  const result = await primersFromTextFile(file, ['primer1']);
208
-
208
+
209
209
  expect(result).toEqual([
210
210
  { name: 'primer1', sequence: 'XYZ', error: 'existing' }
211
211
  ]);
@@ -215,7 +215,7 @@ describe('primersFromTextFile', () => {
215
215
  const file = createFile('name,sequence\nprimer1,ATGC\nprimer2,XYZ\nprimer3,CGTG', 'test.csv');
216
216
 
217
217
  const result = await primersFromTextFile(file, ['primer1']);
218
-
218
+
219
219
  expect(result).toEqual([
220
220
  { name: 'primer1', sequence: 'ATGC', error: 'existing' },
221
221
  { name: 'primer2', sequence: 'XYZ', error: 'invalid' },
@@ -233,7 +233,7 @@ describe('primersFromTextFile', () => {
233
233
  const file = createFile('name,sequence\nprimer1,ATGC', 'test.csv');
234
234
 
235
235
  const result = await primersFromTextFile(file, []);
236
-
236
+
237
237
  expect(result).toEqual([
238
238
  { name: 'primer1', sequence: 'ATGC', error: '' }
239
239
  ]);
@@ -243,7 +243,7 @@ describe('primersFromTextFile', () => {
243
243
  const file = createFile('name\tsequence\nprimer1\tATGC', 'test.tsv');
244
244
 
245
245
  const result = await primersFromTextFile(file, []);
246
-
246
+
247
247
  expect(result).toEqual([
248
248
  { name: 'primer1', sequence: 'ATGC', error: '' }
249
249
  ]);
@@ -158,6 +158,26 @@ export const downloadCloningStrategyAsSvg = async (fileName) => {
158
158
  downloadTextFile(svgString, fileName);
159
159
  };
160
160
 
161
+ /**
162
+ * Extract the files of a zip whose path (e.g. 'run/A1/A1.fastq') matches filter, as Files named like the
163
+ * file without its folder (e.g. 'A1.fastq'). Directories, hidden files and folders, and anything in
164
+ * __MACOSX/ (e.g. the ._A1.fastq copies that macOS adds) are skipped.
165
+ */
166
+ export async function extractFilesFromZip(zipFile, filter) {
167
+ const zipReader = new ZipReader(new BlobReader(zipFile));
168
+ try {
169
+ const entries = (await zipReader.getEntries()).filter((entry) => !entry.directory
170
+ && !entry.filename.split('/').some((part) => part.startsWith('.') || part === '__MACOSX')
171
+ && filter(entry.filename));
172
+ return await Promise.all(entries.map(async (entry) => new File(
173
+ [await entry.getData(new BlobWriter())],
174
+ entry.filename.split('/').pop(),
175
+ )));
176
+ } finally {
177
+ await zipReader.close();
178
+ }
179
+ }
180
+
161
181
  export async function parseHistoryFile(file) {
162
182
  const isZipFile = file.name.endsWith('.zip');
163
183
  const isJsonFile = file.name.endsWith('.json');
@@ -279,6 +299,16 @@ export async function getTeselaJsonFromBase64(ab1Base64, fileName = null) {
279
299
  }
280
300
  }
281
301
 
302
+ /**
303
+ * Read a sequencing file (ab1, fasta, genbank, fastq...) and parse it.
304
+ * Returns the base64 content (to store it) and the parsed teselaJson (e.g. teselaJson.sequence is the trace).
305
+ */
306
+ export async function readSequencingFile(file) {
307
+ const base64str = await file2base64(file);
308
+ const teselaJson = await getTeselaJsonFromBase64(base64str, file.name);
309
+ return { base64str, teselaJson };
310
+ }
311
+
282
312
  export function formatTemplate(data, url) {
283
313
  const newData = { ...data };
284
314
  const segments = url.split('/');