@apollo-annotation/common 0.1.18 → 0.1.19
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 +5 -5
- package/src/AssemblySpecificChange.ts +0 -444
- package/src/Change.ts +7 -3
- package/src/Operation.ts +2 -0
- package/tsconfig.json +1 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apollo-annotation/common",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.19",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/GMOD/Apollo3.git",
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
"build": "tsc"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@apollo-annotation/schemas": "^0.1.
|
|
14
|
+
"@apollo-annotation/schemas": "^0.1.19",
|
|
15
15
|
"@gmod/gff": "1.2.0",
|
|
16
|
-
"@jbrowse/core": "^2.
|
|
16
|
+
"@jbrowse/core": "^2.13.1",
|
|
17
17
|
"bson-objectid": "^2.0.4",
|
|
18
18
|
"tslib": "^2.3.1"
|
|
19
19
|
},
|
|
@@ -30,12 +30,12 @@
|
|
|
30
30
|
"tss-react": "^4.6.1"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@apollo-annotation/mst": "^0.1.
|
|
33
|
+
"@apollo-annotation/mst": "^0.1.19",
|
|
34
34
|
"@nestjs/common": "^10.1.0",
|
|
35
35
|
"@nestjs/core": "^10.1.0",
|
|
36
36
|
"@types/node": "^18.14.2",
|
|
37
37
|
"mongoose": "^6.12.0",
|
|
38
|
-
"typescript": "^5.
|
|
38
|
+
"typescript": "^5.5.3"
|
|
39
39
|
},
|
|
40
40
|
"publishConfig": {
|
|
41
41
|
"access": "public"
|
|
@@ -1,16 +1,5 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
|
2
|
-
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
3
|
-
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
4
|
-
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
|
5
|
-
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
6
|
-
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
|
7
|
-
import type { AnnotationFeatureSnapshot } from '@apollo-annotation/mst'
|
|
8
|
-
import { FileDocument, RefSeqDocument } from '@apollo-annotation/schemas'
|
|
9
|
-
import { GFF3Feature } from '@gmod/gff'
|
|
10
|
-
import ObjectID from 'bson-objectid'
|
|
11
|
-
|
|
12
2
|
import { Change, ChangeOptions, SerializedChange, isChange } from './Change'
|
|
13
|
-
import { ServerDataStore } from './Operation'
|
|
14
3
|
|
|
15
4
|
export interface SerializedAssemblySpecificChange extends SerializedChange {
|
|
16
5
|
assembly: string
|
|
@@ -32,437 +21,4 @@ export abstract class AssemblySpecificChange extends Change {
|
|
|
32
21
|
super(json, options)
|
|
33
22
|
this.assembly = json.assembly
|
|
34
23
|
}
|
|
35
|
-
|
|
36
|
-
async addRefSeqIntoDb(
|
|
37
|
-
fileDoc: FileDocument,
|
|
38
|
-
assembly: string,
|
|
39
|
-
backend: ServerDataStore,
|
|
40
|
-
) {
|
|
41
|
-
const { logger } = this
|
|
42
|
-
const { filesService, refSeqChunkModel, refSeqModel, user } = backend
|
|
43
|
-
const { CHUNK_SIZE } = process.env
|
|
44
|
-
const customChunkSize = CHUNK_SIZE && Number(CHUNK_SIZE)
|
|
45
|
-
let chunkIndex = 0
|
|
46
|
-
let refSeqLen = 0
|
|
47
|
-
let refSeqDoc: RefSeqDocument | undefined
|
|
48
|
-
let fastaInfoStarted = fileDoc.type !== 'text/x-gff3'
|
|
49
|
-
|
|
50
|
-
// Read data from compressed file and parse the content
|
|
51
|
-
const sequenceStream = filesService.getFileStream(fileDoc)
|
|
52
|
-
let sequenceBuffer = ''
|
|
53
|
-
let incompleteLine = ''
|
|
54
|
-
let lastLineIsIncomplete = true
|
|
55
|
-
let parsingStarted = false
|
|
56
|
-
logger.debug?.('starting sequence stream')
|
|
57
|
-
for await (const data of sequenceStream) {
|
|
58
|
-
const chunk = data.toString()
|
|
59
|
-
lastLineIsIncomplete = !chunk.endsWith('\n')
|
|
60
|
-
// chunk is small enough that you can split the whole thing into lines without having to make it into smaller chunks first.
|
|
61
|
-
const lines = chunk.split(/\r?\n/)
|
|
62
|
-
if (incompleteLine) {
|
|
63
|
-
lines[0] = `${incompleteLine}${lines[0]}`
|
|
64
|
-
incompleteLine = ''
|
|
65
|
-
}
|
|
66
|
-
if (lastLineIsIncomplete) {
|
|
67
|
-
incompleteLine = lines.pop() || ''
|
|
68
|
-
}
|
|
69
|
-
for await (const line of lines) {
|
|
70
|
-
// In case of GFF3 file we start to read sequence after '##FASTA' is found
|
|
71
|
-
if (!fastaInfoStarted) {
|
|
72
|
-
if (line.trim() === '##FASTA') {
|
|
73
|
-
fastaInfoStarted = true
|
|
74
|
-
}
|
|
75
|
-
continue
|
|
76
|
-
}
|
|
77
|
-
const refSeqInfoLine = /^>\s*(\S+)\s*(.*)/.exec(line)
|
|
78
|
-
// Add new ref sequence info if we are reference seq info line
|
|
79
|
-
if (refSeqInfoLine) {
|
|
80
|
-
parsingStarted = true
|
|
81
|
-
logger.debug?.(
|
|
82
|
-
`Reference sequence information line "${refSeqInfoLine}"`,
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
// If there is sequence from previous reference sequence then we need to add it to previous ref seq
|
|
86
|
-
if (sequenceBuffer !== '') {
|
|
87
|
-
if (!refSeqDoc) {
|
|
88
|
-
throw new Error('No refSeq document found')
|
|
89
|
-
}
|
|
90
|
-
refSeqLen += sequenceBuffer.length
|
|
91
|
-
logger.debug?.(
|
|
92
|
-
`Creating refSeq chunk number ${chunkIndex} of "${refSeqDoc._id}"`,
|
|
93
|
-
)
|
|
94
|
-
// We cannot use Mongo 'session' / transaction here because Mongo has 16 MB limit for transaction
|
|
95
|
-
await refSeqChunkModel.create([
|
|
96
|
-
{
|
|
97
|
-
refSeq: refSeqDoc._id,
|
|
98
|
-
n: chunkIndex,
|
|
99
|
-
sequence: sequenceBuffer,
|
|
100
|
-
user,
|
|
101
|
-
status: -1,
|
|
102
|
-
},
|
|
103
|
-
])
|
|
104
|
-
sequenceBuffer = ''
|
|
105
|
-
}
|
|
106
|
-
await refSeqDoc?.updateOne({ length: refSeqLen })
|
|
107
|
-
// await refSeqDoc?.updateOne({ length: refSeqLen }, { session })
|
|
108
|
-
refSeqLen = 0
|
|
109
|
-
chunkIndex = 0
|
|
110
|
-
|
|
111
|
-
const name = refSeqInfoLine[1].trim()
|
|
112
|
-
const description = refSeqInfoLine[2] ? refSeqInfoLine[2].trim() : ''
|
|
113
|
-
|
|
114
|
-
// We cannot use Mongo 'session' / transaction here because Mongo has 16 MB limit for transaction
|
|
115
|
-
const [newRefSeqDoc] = await refSeqModel.create([
|
|
116
|
-
{
|
|
117
|
-
name,
|
|
118
|
-
description,
|
|
119
|
-
assembly,
|
|
120
|
-
length: 0,
|
|
121
|
-
...(customChunkSize ? { chunkSize: customChunkSize } : null),
|
|
122
|
-
user,
|
|
123
|
-
status: -1,
|
|
124
|
-
},
|
|
125
|
-
])
|
|
126
|
-
logger.debug?.(
|
|
127
|
-
`Added new refSeq "${name}", desc "${description}", docId "${newRefSeqDoc._id}"`,
|
|
128
|
-
)
|
|
129
|
-
refSeqDoc = newRefSeqDoc
|
|
130
|
-
} else if (/\S/.test(line)) {
|
|
131
|
-
if (!refSeqDoc) {
|
|
132
|
-
throw new Error('No refSeq document found')
|
|
133
|
-
}
|
|
134
|
-
const { _id, chunkSize } = refSeqDoc
|
|
135
|
-
sequenceBuffer += line.replaceAll(/\s/g, '')
|
|
136
|
-
// If sequence block > chunk size then save chunk into Mongo
|
|
137
|
-
while (sequenceBuffer.length >= chunkSize) {
|
|
138
|
-
const sequence = sequenceBuffer.slice(0, chunkSize)
|
|
139
|
-
refSeqLen += sequence.length
|
|
140
|
-
logger.debug?.(
|
|
141
|
-
`Creating refSeq chunk number ${chunkIndex} of "${_id}"`,
|
|
142
|
-
)
|
|
143
|
-
// We cannot use Mongo 'session' / transaction here because Mongo has 16 MB limit for transaction
|
|
144
|
-
await refSeqChunkModel.create([
|
|
145
|
-
{ refSeq: _id, n: chunkIndex, sequence, user, status: -1 },
|
|
146
|
-
])
|
|
147
|
-
chunkIndex++
|
|
148
|
-
// Set remaining sequence
|
|
149
|
-
sequenceBuffer = sequenceBuffer.slice(chunkSize)
|
|
150
|
-
logger.debug?.(`Remaining sequence: "${sequenceBuffer}"`)
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
if (!parsingStarted) {
|
|
156
|
-
throw new Error('No reference sequences found in file')
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (sequenceBuffer || lastLineIsIncomplete) {
|
|
160
|
-
if (!refSeqDoc) {
|
|
161
|
-
throw new Error('No refSeq document found')
|
|
162
|
-
}
|
|
163
|
-
// If the file did not end with line break so the last line is incomplete
|
|
164
|
-
if (lastLineIsIncomplete) {
|
|
165
|
-
sequenceBuffer += incompleteLine
|
|
166
|
-
}
|
|
167
|
-
refSeqLen += sequenceBuffer.length
|
|
168
|
-
logger.verbose?.(
|
|
169
|
-
`*** Add the very last chunk to ref seq ("${refSeqDoc._id}", index ${chunkIndex} and total length for ref seq is ${refSeqLen}): "${sequenceBuffer}"`,
|
|
170
|
-
)
|
|
171
|
-
logger.debug?.(
|
|
172
|
-
`Creating refSeq chunk number ${chunkIndex} of "${refSeqDoc._id}"`,
|
|
173
|
-
)
|
|
174
|
-
// We cannot use Mongo 'session' / transaction here because Mongo has 16 MB limit for transaction
|
|
175
|
-
await refSeqChunkModel.create([
|
|
176
|
-
{
|
|
177
|
-
refSeq: refSeqDoc._id,
|
|
178
|
-
n: chunkIndex,
|
|
179
|
-
sequence: sequenceBuffer,
|
|
180
|
-
user,
|
|
181
|
-
status: -1,
|
|
182
|
-
},
|
|
183
|
-
])
|
|
184
|
-
await refSeqDoc.updateOne({ length: refSeqLen })
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
private refSeqCache = new Map<string, RefSeqDocument>()
|
|
189
|
-
|
|
190
|
-
async removeExistingFeatures(backend: ServerDataStore) {
|
|
191
|
-
const { featureModel, refSeqModel } = backend
|
|
192
|
-
const { assembly, logger } = this
|
|
193
|
-
logger.debug?.(`Removing existing features for assembly = ${assembly}`)
|
|
194
|
-
|
|
195
|
-
const refSeqs: RefSeqDocument[] = await refSeqModel
|
|
196
|
-
.find({ assembly })
|
|
197
|
-
.exec()
|
|
198
|
-
|
|
199
|
-
for (const refSeq of refSeqs) {
|
|
200
|
-
await featureModel.deleteMany({ refSeq: refSeq._id })
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
async addFeatureIntoDb(gff3Feature: GFF3Feature, backend: ServerDataStore) {
|
|
205
|
-
const { featureModel, refSeqModel, user } = backend
|
|
206
|
-
const { assembly, logger, refSeqCache } = this
|
|
207
|
-
|
|
208
|
-
const [{ seq_id: refName }] = gff3Feature
|
|
209
|
-
if (!refName) {
|
|
210
|
-
throw new Error(
|
|
211
|
-
`Valid seq_id not found in feature ${JSON.stringify(gff3Feature)}`,
|
|
212
|
-
)
|
|
213
|
-
}
|
|
214
|
-
let refSeqDoc = refSeqCache.get(refName)
|
|
215
|
-
if (!refSeqDoc) {
|
|
216
|
-
refSeqDoc =
|
|
217
|
-
(await refSeqModel.findOne({ assembly, name: refName }).exec()) ??
|
|
218
|
-
undefined
|
|
219
|
-
if (refSeqDoc) {
|
|
220
|
-
refSeqCache.set(refName, refSeqDoc)
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
if (!refSeqDoc) {
|
|
224
|
-
throw new Error(
|
|
225
|
-
`RefSeq was not found by assembly "${assembly}" and seq_id "${refName}" not found`,
|
|
226
|
-
)
|
|
227
|
-
}
|
|
228
|
-
// Let's add featureId to parent feature
|
|
229
|
-
const featureIds: string[] = []
|
|
230
|
-
|
|
231
|
-
const newFeature = createFeature(gff3Feature, refSeqDoc._id, featureIds)
|
|
232
|
-
logger.debug?.(`So far feature ids are: ${featureIds.toString()}`)
|
|
233
|
-
// Add value to gffId
|
|
234
|
-
newFeature.attributes?._id
|
|
235
|
-
? (newFeature.gffId = newFeature.attributes?._id.toString())
|
|
236
|
-
: (newFeature.gffId = newFeature._id)
|
|
237
|
-
logger.debug?.(
|
|
238
|
-
`********************* Assembly specific change create ${JSON.stringify(
|
|
239
|
-
newFeature,
|
|
240
|
-
)}`,
|
|
241
|
-
)
|
|
242
|
-
|
|
243
|
-
// Add into Mongo
|
|
244
|
-
// We cannot use Mongo 'session' / transaction here because Mongo has 16 MB limit for transaction
|
|
245
|
-
const [newFeatureDoc] = await featureModel.create([
|
|
246
|
-
{ allIds: featureIds, ...newFeature, user, status: -1 },
|
|
247
|
-
])
|
|
248
|
-
logger.verbose?.(`Added docId "${newFeatureDoc._id}"`)
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function createFeature(
|
|
253
|
-
gff3Feature: GFF3Feature,
|
|
254
|
-
refSeq: string,
|
|
255
|
-
featureIds?: string[],
|
|
256
|
-
): AnnotationFeatureSnapshot {
|
|
257
|
-
const [firstFeature] = gff3Feature
|
|
258
|
-
const {
|
|
259
|
-
attributes,
|
|
260
|
-
child_features: childFeatures,
|
|
261
|
-
end,
|
|
262
|
-
phase,
|
|
263
|
-
score,
|
|
264
|
-
seq_id: refName,
|
|
265
|
-
source,
|
|
266
|
-
start,
|
|
267
|
-
strand,
|
|
268
|
-
type,
|
|
269
|
-
} = firstFeature
|
|
270
|
-
if (!refName) {
|
|
271
|
-
throw new Error(
|
|
272
|
-
`feature does not have seq_id: ${JSON.stringify(firstFeature)}`,
|
|
273
|
-
)
|
|
274
|
-
}
|
|
275
|
-
if (!type) {
|
|
276
|
-
throw new Error(
|
|
277
|
-
`feature does not have type: ${JSON.stringify(firstFeature)}`,
|
|
278
|
-
)
|
|
279
|
-
}
|
|
280
|
-
if (start === null) {
|
|
281
|
-
throw new Error(
|
|
282
|
-
`feature does not have start: ${JSON.stringify(firstFeature)}`,
|
|
283
|
-
)
|
|
284
|
-
}
|
|
285
|
-
if (end === null) {
|
|
286
|
-
throw new Error(
|
|
287
|
-
`feature does not have end: ${JSON.stringify(firstFeature)}`,
|
|
288
|
-
)
|
|
289
|
-
}
|
|
290
|
-
const feature: AnnotationFeatureSnapshot = {
|
|
291
|
-
_id: new ObjectID().toHexString(),
|
|
292
|
-
gffId: '',
|
|
293
|
-
refSeq,
|
|
294
|
-
type,
|
|
295
|
-
start: start - 1,
|
|
296
|
-
end,
|
|
297
|
-
}
|
|
298
|
-
if (gff3Feature.length > 1) {
|
|
299
|
-
const lastEnd = Math.max(
|
|
300
|
-
...gff3Feature.map((f) => {
|
|
301
|
-
if (f.end === null) {
|
|
302
|
-
throw new Error(`feature does not have end: ${JSON.stringify(f)}`)
|
|
303
|
-
}
|
|
304
|
-
return f.end
|
|
305
|
-
}),
|
|
306
|
-
)
|
|
307
|
-
feature.end = lastEnd
|
|
308
|
-
feature.discontinuousLocations = gff3Feature.map((f) => {
|
|
309
|
-
const { end: subEnd, phase: locationPhase, start: subStart } = f
|
|
310
|
-
if (subStart === null || subEnd === null) {
|
|
311
|
-
throw new Error(
|
|
312
|
-
`feature does not have start and/or end: ${JSON.stringify(f)}`,
|
|
313
|
-
)
|
|
314
|
-
}
|
|
315
|
-
let parsedPhase: 0 | 1 | 2 | undefined
|
|
316
|
-
if (locationPhase) {
|
|
317
|
-
switch (locationPhase) {
|
|
318
|
-
case '0': {
|
|
319
|
-
parsedPhase = 0
|
|
320
|
-
|
|
321
|
-
break
|
|
322
|
-
}
|
|
323
|
-
case '1': {
|
|
324
|
-
parsedPhase = 1
|
|
325
|
-
|
|
326
|
-
break
|
|
327
|
-
}
|
|
328
|
-
case '2': {
|
|
329
|
-
parsedPhase = 2
|
|
330
|
-
|
|
331
|
-
break
|
|
332
|
-
}
|
|
333
|
-
default: {
|
|
334
|
-
throw new Error(`Unknown phase: "${locationPhase}"`)
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
return { start: subStart - 1, end: subEnd, phase: parsedPhase }
|
|
339
|
-
})
|
|
340
|
-
}
|
|
341
|
-
if (strand) {
|
|
342
|
-
if (strand === '+') {
|
|
343
|
-
feature.strand = 1
|
|
344
|
-
} else if (strand === '-') {
|
|
345
|
-
feature.strand = -1
|
|
346
|
-
} else {
|
|
347
|
-
throw new Error(`Unknown strand: "${strand}"`)
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
if (score !== null) {
|
|
351
|
-
feature.score = score
|
|
352
|
-
}
|
|
353
|
-
if (phase) {
|
|
354
|
-
switch (phase) {
|
|
355
|
-
case '0': {
|
|
356
|
-
feature.phase = 0
|
|
357
|
-
|
|
358
|
-
break
|
|
359
|
-
}
|
|
360
|
-
case '1': {
|
|
361
|
-
feature.phase = 1
|
|
362
|
-
|
|
363
|
-
break
|
|
364
|
-
}
|
|
365
|
-
case '2': {
|
|
366
|
-
feature.phase = 2
|
|
367
|
-
|
|
368
|
-
break
|
|
369
|
-
}
|
|
370
|
-
default: {
|
|
371
|
-
throw new Error(`Unknown phase: "${phase}"`)
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
if (featureIds) {
|
|
376
|
-
featureIds.push(feature._id)
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
if (childFeatures?.length) {
|
|
380
|
-
const children: Record<string, AnnotationFeatureSnapshot> = {}
|
|
381
|
-
for (const childFeature of childFeatures) {
|
|
382
|
-
const child = createFeature(childFeature, refSeq, featureIds)
|
|
383
|
-
children[child._id] = child
|
|
384
|
-
// Add value to gffId
|
|
385
|
-
child.attributes?._id
|
|
386
|
-
? (child.gffId = child.attributes?._id.toString())
|
|
387
|
-
: (child.gffId = child._id)
|
|
388
|
-
}
|
|
389
|
-
feature.children = children
|
|
390
|
-
}
|
|
391
|
-
if (source ?? attributes) {
|
|
392
|
-
const attrs: Record<string, string[]> = {}
|
|
393
|
-
if (source) {
|
|
394
|
-
attrs.source = [source]
|
|
395
|
-
}
|
|
396
|
-
if (attributes) {
|
|
397
|
-
for (const [key, val] of Object.entries(attributes)) {
|
|
398
|
-
if (val) {
|
|
399
|
-
const newKey = key.toLowerCase()
|
|
400
|
-
if (newKey !== 'parent') {
|
|
401
|
-
// attrs[key.toLowerCase()] = val
|
|
402
|
-
switch (key) {
|
|
403
|
-
case 'ID': {
|
|
404
|
-
attrs._id = val
|
|
405
|
-
break
|
|
406
|
-
}
|
|
407
|
-
case 'Name': {
|
|
408
|
-
attrs.gff_name = val
|
|
409
|
-
break
|
|
410
|
-
}
|
|
411
|
-
case 'Alias': {
|
|
412
|
-
attrs.gff_alias = val
|
|
413
|
-
break
|
|
414
|
-
}
|
|
415
|
-
case 'Target': {
|
|
416
|
-
attrs.gff_target = val
|
|
417
|
-
break
|
|
418
|
-
}
|
|
419
|
-
case 'Gap': {
|
|
420
|
-
attrs.gff_gap = val
|
|
421
|
-
break
|
|
422
|
-
}
|
|
423
|
-
case 'Derives_from': {
|
|
424
|
-
attrs.gff_derives_from = val
|
|
425
|
-
break
|
|
426
|
-
}
|
|
427
|
-
case 'Note': {
|
|
428
|
-
attrs.gff_note = val
|
|
429
|
-
break
|
|
430
|
-
}
|
|
431
|
-
case 'Dbxref': {
|
|
432
|
-
attrs.gff_dbxref = val
|
|
433
|
-
break
|
|
434
|
-
}
|
|
435
|
-
case 'Ontology_term': {
|
|
436
|
-
const goTerms: string[] = []
|
|
437
|
-
const otherTerms: string[] = []
|
|
438
|
-
for (const v of val) {
|
|
439
|
-
if (v.startsWith('GO:')) {
|
|
440
|
-
goTerms.push(v)
|
|
441
|
-
} else {
|
|
442
|
-
otherTerms.push(v)
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
if (goTerms.length > 0) {
|
|
446
|
-
attrs['Gene Ontology'] = goTerms
|
|
447
|
-
}
|
|
448
|
-
if (otherTerms.length > 0) {
|
|
449
|
-
attrs.gff_ontology_term = otherTerms
|
|
450
|
-
}
|
|
451
|
-
break
|
|
452
|
-
}
|
|
453
|
-
case 'Is_circular': {
|
|
454
|
-
attrs.gff_is_circular = val
|
|
455
|
-
break
|
|
456
|
-
}
|
|
457
|
-
default: {
|
|
458
|
-
attrs[key.toLowerCase()] = val
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
feature.attributes = attrs
|
|
466
|
-
}
|
|
467
|
-
return feature
|
|
468
24
|
}
|
package/src/Change.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
|
2
2
|
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
|
3
3
|
import {
|
|
4
|
-
|
|
4
|
+
AnnotationFeature,
|
|
5
5
|
AnnotationFeatureSnapshot,
|
|
6
6
|
ApolloAssemblyI,
|
|
7
|
+
BackendDriverType,
|
|
7
8
|
CheckResultI,
|
|
8
9
|
CheckResultSnapshot,
|
|
9
10
|
} from '@apollo-annotation/mst'
|
|
@@ -28,12 +29,15 @@ export interface ClientDataStore {
|
|
|
28
29
|
): AppRootModel['internetAccounts'][0]
|
|
29
30
|
loadFeatures(regions: Region[]): void
|
|
30
31
|
loadRefSeq(regions: Region[]): void
|
|
31
|
-
getFeature(featureId: string):
|
|
32
|
+
getFeature(featureId: string): AnnotationFeature | undefined
|
|
32
33
|
addFeature(assemblyId: string, feature: AnnotationFeatureSnapshot): void
|
|
33
34
|
deleteFeature(featureId: string): void
|
|
34
35
|
deleteAssembly(assemblyId: string): void
|
|
35
36
|
addCheckResults(checkResults: CheckResultSnapshot[]): void
|
|
36
|
-
addAssembly(
|
|
37
|
+
addAssembly(
|
|
38
|
+
assemblyId: string,
|
|
39
|
+
backendDriverType?: BackendDriverType,
|
|
40
|
+
): ApolloAssemblyI
|
|
37
41
|
}
|
|
38
42
|
|
|
39
43
|
export type SerializedChange = SerializedOperation
|
package/src/Operation.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
FileDocument,
|
|
11
11
|
RefSeqChunkDocument,
|
|
12
12
|
RefSeqDocument,
|
|
13
|
+
JBrowseConfigDocument,
|
|
13
14
|
UserDocument,
|
|
14
15
|
} from '@apollo-annotation/schemas'
|
|
15
16
|
import type { LoggerService } from '@nestjs/common'
|
|
@@ -36,6 +37,7 @@ export interface ServerDataStore {
|
|
|
36
37
|
refSeqChunkModel: Model<RefSeqChunkDocument>
|
|
37
38
|
fileModel: Model<FileDocument>
|
|
38
39
|
userModel: Model<UserDocument>
|
|
40
|
+
jbrowseConfigModel: Model<JBrowseConfigDocument>
|
|
39
41
|
session: ClientSession
|
|
40
42
|
filesService: {
|
|
41
43
|
getFileStream(file: FileDocument): ReadStream
|