@apollo-annotation/shared 0.1.18 → 0.1.20
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/dist/Changes/AddAssemblyAndFeaturesFromFileChange.d.ts +33 -0
- package/dist/Changes/AddAssemblyAndFeaturesFromFileChange.js +83 -0
- package/dist/Changes/AddAssemblyAndFeaturesFromFileChange.js.map +1 -0
- package/dist/Changes/AddAssemblyFromExternalChange.d.ts +34 -0
- package/dist/Changes/AddAssemblyFromExternalChange.js +97 -0
- package/dist/Changes/AddAssemblyFromExternalChange.js.map +1 -0
- package/dist/Changes/AddAssemblyFromFileChange.d.ts +43 -0
- package/dist/Changes/AddAssemblyFromFileChange.js +140 -0
- package/dist/Changes/AddAssemblyFromFileChange.js.map +1 -0
- package/dist/Changes/AddFeatureChange.d.ts +35 -0
- package/dist/Changes/AddFeatureChange.js +169 -0
- package/dist/Changes/AddFeatureChange.js.map +1 -0
- package/dist/Changes/AddFeaturesFromFileChange.d.ts +32 -0
- package/dist/Changes/AddFeaturesFromFileChange.js +76 -0
- package/dist/Changes/AddFeaturesFromFileChange.js.map +1 -0
- package/dist/Changes/AddRefSeqAliasesChange.d.ts +20 -0
- package/dist/Changes/AddRefSeqAliasesChange.js +61 -0
- package/dist/Changes/AddRefSeqAliasesChange.js.map +1 -0
- package/dist/Changes/DeleteAssemblyChange.d.ts +18 -0
- package/dist/Changes/DeleteAssemblyChange.js +72 -0
- package/dist/Changes/DeleteAssemblyChange.js.map +1 -0
- package/dist/Changes/DeleteFeatureChange.d.ts +42 -0
- package/dist/Changes/DeleteFeatureChange.js +143 -0
- package/dist/Changes/DeleteFeatureChange.js.map +1 -0
- package/dist/Changes/DeleteUserChange.d.ts +25 -0
- package/dist/Changes/DeleteUserChange.js +53 -0
- package/dist/Changes/DeleteUserChange.js.map +1 -0
- package/dist/Changes/FeatureAttributeChange.d.ts +31 -0
- package/dist/Changes/FeatureAttributeChange.js +112 -0
- package/dist/Changes/FeatureAttributeChange.js.map +1 -0
- package/dist/Changes/FromFileBaseChange.d.ts +9 -0
- package/dist/Changes/FromFileBaseChange.js +187 -0
- package/dist/Changes/FromFileBaseChange.js.map +1 -0
- package/dist/Changes/ImportJBrowseConfigChange.d.ts +43 -0
- package/dist/Changes/ImportJBrowseConfigChange.js +67 -0
- package/dist/Changes/ImportJBrowseConfigChange.js.map +1 -0
- package/dist/Changes/LocationEndChange.d.ts +32 -0
- package/dist/Changes/LocationEndChange.js +129 -0
- package/dist/Changes/LocationEndChange.js.map +1 -0
- package/dist/Changes/LocationStartChange.d.ts +32 -0
- package/dist/Changes/LocationStartChange.js +129 -0
- package/dist/Changes/LocationStartChange.js.map +1 -0
- package/dist/Changes/StrandChange.d.ts +31 -0
- package/dist/Changes/StrandChange.js +114 -0
- package/dist/Changes/StrandChange.js.map +1 -0
- package/dist/Changes/TypeChange.d.ts +31 -0
- package/dist/Changes/TypeChange.js +114 -0
- package/dist/Changes/TypeChange.js.map +1 -0
- package/dist/Changes/UserChange.d.ts +25 -0
- package/dist/Changes/UserChange.js +51 -0
- package/dist/Changes/UserChange.js.map +1 -0
- package/dist/Changes/index.d.ts +50 -0
- package/dist/Changes/index.js +55 -0
- package/dist/Changes/index.js.map +1 -0
- package/dist/Checks/CDSCheck.d.ts +8 -0
- package/dist/Checks/CDSCheck.js +197 -0
- package/dist/Checks/CDSCheck.js.map +1 -0
- package/dist/Checks/index.d.ts +1 -0
- package/dist/Checks/index.js +5 -0
- package/dist/Checks/index.js.map +1 -0
- package/dist/Common/index.d.ts +1 -0
- package/dist/Common/index.js +5 -0
- package/dist/Common/index.js.map +1 -0
- package/dist/Common/jwtPayload.d.ts +12 -0
- package/dist/Common/jwtPayload.js +16 -0
- package/dist/Common/jwtPayload.js.map +1 -0
- package/dist/GFF3/gff3ToAnnotationFeature.d.ts +3 -0
- package/dist/GFF3/gff3ToAnnotationFeature.js +211 -0
- package/dist/GFF3/gff3ToAnnotationFeature.js.map +1 -0
- package/dist/GFF3/gff3ToAnnotationFeature.test.d.ts +1 -0
- package/dist/GFF3/gff3ToAnnotationFeature.test.js +174 -0
- package/dist/GFF3/gff3ToAnnotationFeature.test.js.map +1 -0
- package/dist/GFF3/gffReservedKeys.d.ts +5 -0
- package/dist/GFF3/gffReservedKeys.js +34 -0
- package/dist/GFF3/gffReservedKeys.js.map +1 -0
- package/dist/GFF3/index.d.ts +2 -0
- package/dist/GFF3/index.js +6 -0
- package/dist/GFF3/index.js.map +1 -0
- package/dist/Messages.d.ts +28 -0
- package/dist/Messages.js +3 -0
- package/dist/Messages.js.map +1 -0
- package/dist/Operations/GetAssembliesOperation.d.ts +13 -0
- package/dist/Operations/GetAssembliesOperation.js +20 -0
- package/dist/Operations/GetAssembliesOperation.js.map +1 -0
- package/dist/Operations/GetFeaturesOperation.d.ts +26 -0
- package/dist/Operations/GetFeaturesOperation.js +42 -0
- package/dist/Operations/GetFeaturesOperation.js.map +1 -0
- package/dist/Operations/index.d.ts +8 -0
- package/dist/Operations/index.js +10 -0
- package/dist/Operations/index.js.map +1 -0
- package/dist/Validations/CoreValidation.d.ts +17 -0
- package/dist/Validations/CoreValidation.js +36 -0
- package/dist/Validations/CoreValidation.js.map +1 -0
- package/dist/Validations/ParentChildValidation.d.ts +17 -0
- package/dist/Validations/ParentChildValidation.js +61 -0
- package/dist/Validations/ParentChildValidation.js.map +1 -0
- package/dist/Validations/Validation.d.ts +27 -0
- package/dist/Validations/Validation.js +27 -0
- package/dist/Validations/Validation.js.map +1 -0
- package/dist/Validations/ValidationSet.d.ts +24 -0
- package/dist/Validations/ValidationSet.js +86 -0
- package/dist/Validations/ValidationSet.js.map +1 -0
- package/dist/Validations/index.d.ts +4 -0
- package/dist/Validations/index.js +8 -0
- package/dist/Validations/index.js.map +1 -0
- package/dist/Validations/soSequenceTypes.d.ts +2 -0
- package/dist/Validations/soSequenceTypes.js +1868 -0
- package/dist/Validations/soSequenceTypes.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/util.d.ts +4 -0
- package/dist/util.js +102 -0
- package/dist/util.js.map +1 -0
- package/package.json +17 -8
- package/src/Changes/AddAssemblyAndFeaturesFromFileChange.ts +13 -9
- package/src/Changes/AddAssemblyFromFileChange.ts +129 -43
- package/src/Changes/AddFeatureChange.ts +2 -3
- package/src/Changes/AddFeaturesFromFileChange.ts +8 -3
- package/src/Changes/AddRefSeqAliasesChange.ts +88 -0
- package/src/Changes/DeleteAssemblyChange.ts +1 -1
- package/src/Changes/DeleteFeatureChange.ts +1 -1
- package/src/Changes/FeatureAttributeChange.ts +1 -1
- package/src/Changes/FromFileBaseChange.ts +221 -0
- package/src/Changes/ImportJBrowseConfigChange.ts +116 -0
- package/src/Changes/LocationEndChange.ts +25 -18
- package/src/Changes/LocationStartChange.ts +25 -18
- package/src/Changes/TypeChange.ts +1 -1
- package/src/Changes/index.ts +6 -6
- package/src/Checks/CDSCheck.ts +125 -173
- package/src/Common/jwtPayload.ts +1 -1
- package/src/GFF3/gff3ToAnnotationFeature.test.ts +209 -0
- package/src/GFF3/gff3ToAnnotationFeature.ts +257 -0
- package/src/GFF3/gffReservedKeys.ts +61 -0
- package/src/GFF3/index.ts +2 -0
- package/src/Operations/GetFeaturesOperation.ts +2 -2
- package/src/Validations/ParentChildValidation.ts +5 -5
- package/src/index.ts +1 -0
- package/src/util.ts +16 -12
- package/test_data/braker.gff +13 -0
- package/test_data/example01.gff3 +26 -0
- package/test_data/example01.json +236 -0
- package/test_data/example02.json +225 -0
- package/test_data/example04.json +225 -0
- package/test_data/example05.gff3 +30 -0
- package/test_data/example06.gff3 +30 -0
- package/test_data/example07.gff3 +30 -0
- package/test_data/gene_mrna.gff3 +4 -0
- package/test_data/gene_representations.gff3 +1781 -0
- package/test_data/one_cds.gff3 +9 -0
- package/test_data/one_cds.json +67 -0
- package/test_data/two_cds.gff3 +9 -0
- package/test_data/two_cds.json +67 -0
- package/tsconfig.json +4 -4
- package/src/Changes/DiscontinuousLocationEndChange.ts +0 -182
- package/src/Changes/DiscontinuousLocationStartChange.ts +0 -182
package/src/Checks/CDSCheck.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
AnnotationFeatureSnapshot,
|
|
4
4
|
CheckResultSnapshot,
|
|
5
5
|
} from '@apollo-annotation/mst'
|
|
6
|
+
import { intersection2 } from '@jbrowse/core/util'
|
|
6
7
|
import ObjectID from 'bson-objectid'
|
|
7
8
|
|
|
8
9
|
enum STOP_CODONS {
|
|
@@ -29,6 +30,9 @@ const iupacComplements: Record<string, string | undefined> = {
|
|
|
29
30
|
N /* G or A or T or C */: 'N',
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
type CDSLocation = { min: number; max: number; phase: 0 | 1 | 2 }[]
|
|
34
|
+
type CDSLocations = CDSLocation[]
|
|
35
|
+
|
|
32
36
|
function reverseComplement(dna: string): string {
|
|
33
37
|
const complement: string[] = []
|
|
34
38
|
for (const nt of dna) {
|
|
@@ -45,47 +49,18 @@ function reverseComplement(dna: string): string {
|
|
|
45
49
|
return complement.reverse().join('')
|
|
46
50
|
}
|
|
47
51
|
|
|
48
|
-
async function
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
) {
|
|
52
|
-
let seq = ''
|
|
53
|
-
if (
|
|
54
|
-
feature.discontinuousLocations !== undefined &&
|
|
55
|
-
feature.discontinuousLocations.length > 0
|
|
56
|
-
) {
|
|
57
|
-
for (const loc of feature.discontinuousLocations) {
|
|
58
|
-
seq = seq + (await getSequence(loc.start, loc.end))
|
|
59
|
-
}
|
|
60
|
-
} else {
|
|
61
|
-
seq = await getSequence(feature.start, feature.end)
|
|
62
|
-
}
|
|
63
|
-
if (feature.strand === -1) {
|
|
64
|
-
return reverseComplement(seq)
|
|
65
|
-
}
|
|
66
|
-
return seq
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
async function getSequenceFromMultipleFeatures(
|
|
70
|
-
features: AnnotationFeatureSnapshot[],
|
|
52
|
+
async function getCDSSequence(
|
|
53
|
+
cdsLocation: CDSLocation,
|
|
54
|
+
strand: 1 | -1 | undefined,
|
|
71
55
|
getSequence: (start: number, end: number) => Promise<string>,
|
|
72
|
-
) {
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
.join(', ')}"`,
|
|
79
|
-
)
|
|
80
|
-
}
|
|
81
|
-
let seq = ''
|
|
82
|
-
for (const feature of features) {
|
|
83
|
-
seq = seq + (await getSequence(feature.start, feature.end))
|
|
56
|
+
): Promise<string> {
|
|
57
|
+
const sequences = await Promise.all(
|
|
58
|
+
cdsLocation.map(({ max, min }) => getSequence(min, max)),
|
|
59
|
+
)
|
|
60
|
+
if (strand === -1) {
|
|
61
|
+
return sequences.map((seq) => reverseComplement(seq)).join('')
|
|
84
62
|
}
|
|
85
|
-
|
|
86
|
-
return reverseComplement(seq)
|
|
87
|
-
}
|
|
88
|
-
return seq
|
|
63
|
+
return sequences.join('')
|
|
89
64
|
}
|
|
90
65
|
|
|
91
66
|
function splitSequenceInCodons(cds: string): string[] {
|
|
@@ -97,132 +72,135 @@ function splitSequenceInCodons(cds: string): string[] {
|
|
|
97
72
|
}
|
|
98
73
|
|
|
99
74
|
function getOriginalCodonLocation(
|
|
100
|
-
|
|
75
|
+
cdsLocation: CDSLocation,
|
|
76
|
+
strand: 1 | -1 | undefined,
|
|
101
77
|
index: number,
|
|
102
|
-
): [number, number] {
|
|
78
|
+
): [number, number] | undefined {
|
|
103
79
|
let lengthToStart = index * 3
|
|
104
80
|
let lengthToEnd = lengthToStart + 3
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
if (endLocation === undefined && featureLength > lengthToEnd) {
|
|
116
|
-
endLocation = f.start + lengthToEnd
|
|
117
|
-
} else {
|
|
118
|
-
lengthToEnd -= featureLength
|
|
119
|
-
}
|
|
120
|
-
if (startLocation !== undefined && endLocation !== undefined) {
|
|
121
|
-
return [startLocation, endLocation]
|
|
122
|
-
}
|
|
81
|
+
|
|
82
|
+
let startLocation: number | undefined = undefined,
|
|
83
|
+
endLocation: number | undefined = undefined
|
|
84
|
+
for (const loc of cdsLocation) {
|
|
85
|
+
const locLength = loc.max - loc.min
|
|
86
|
+
if (startLocation === undefined && locLength > lengthToStart) {
|
|
87
|
+
startLocation = loc.min + lengthToStart
|
|
88
|
+
} else {
|
|
89
|
+
lengthToStart -= locLength
|
|
123
90
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (
|
|
127
|
-
feature.discontinuousLocations !== undefined &&
|
|
128
|
-
feature.discontinuousLocations.length > 0
|
|
129
|
-
) {
|
|
130
|
-
let startLocation: number | undefined = undefined,
|
|
131
|
-
endLocation: number | undefined = undefined
|
|
132
|
-
for (const loc of feature.discontinuousLocations) {
|
|
133
|
-
const locLength = loc.end - loc.start
|
|
134
|
-
if (startLocation === undefined && locLength > lengthToStart) {
|
|
135
|
-
startLocation = loc.start + lengthToStart
|
|
136
|
-
} else {
|
|
137
|
-
lengthToStart -= locLength
|
|
138
|
-
}
|
|
139
|
-
if (endLocation === undefined && locLength > lengthToEnd) {
|
|
140
|
-
endLocation = loc.start + lengthToEnd
|
|
141
|
-
} else {
|
|
142
|
-
lengthToEnd -= locLength
|
|
143
|
-
}
|
|
144
|
-
if (startLocation !== undefined && endLocation !== undefined) {
|
|
145
|
-
return [startLocation, endLocation]
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
throw new Error('Could not determine original CDS location')
|
|
91
|
+
if (endLocation === undefined && locLength > lengthToEnd) {
|
|
92
|
+
endLocation = loc.min + lengthToEnd
|
|
149
93
|
} else {
|
|
150
|
-
|
|
94
|
+
lengthToEnd -= locLength
|
|
95
|
+
}
|
|
96
|
+
if (startLocation !== undefined && endLocation !== undefined) {
|
|
97
|
+
return [startLocation, endLocation]
|
|
151
98
|
}
|
|
152
99
|
}
|
|
100
|
+
return
|
|
153
101
|
}
|
|
154
102
|
|
|
155
|
-
async function
|
|
156
|
-
feature: AnnotationFeatureSnapshot
|
|
103
|
+
async function checkMRNA(
|
|
104
|
+
feature: AnnotationFeatureSnapshot,
|
|
157
105
|
getSequence: (start: number, end: number) => Promise<string>,
|
|
158
106
|
): Promise<CheckResultSnapshot[]> {
|
|
159
107
|
const checkResults: CheckResultSnapshot[] = []
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
refSeq: string,
|
|
165
|
-
sequence: string
|
|
166
|
-
if (Array.isArray(feature)) {
|
|
167
|
-
sequence = await getSequenceFromMultipleFeatures(feature, getSequence)
|
|
168
|
-
ids = feature.map((f) => f._id)
|
|
169
|
-
_id = ids.join(',')
|
|
170
|
-
;[{ refSeq, start }] = feature
|
|
171
|
-
const lastFeature = feature.at(-1)
|
|
172
|
-
if (!lastFeature) {
|
|
173
|
-
throw new Error('Zero-length feature array encountered')
|
|
174
|
-
}
|
|
175
|
-
;({ end } = lastFeature)
|
|
176
|
-
} else {
|
|
177
|
-
sequence = await getSequenceFromSingleFeature(feature, getSequence)
|
|
178
|
-
;({ _id, end, refSeq, start } = feature)
|
|
179
|
-
ids = [_id]
|
|
108
|
+
const { _id, max, min, refSeq, strand } = feature
|
|
109
|
+
const cdsLocations = getCDSLocations(feature)
|
|
110
|
+
if (!cdsLocations) {
|
|
111
|
+
return checkResults
|
|
180
112
|
}
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
113
|
+
const ids = [_id]
|
|
114
|
+
for (const cdsLocation of cdsLocations) {
|
|
115
|
+
const sequence = await getCDSSequence(cdsLocation, strand, getSequence)
|
|
116
|
+
const codons = splitSequenceInCodons(sequence)
|
|
117
|
+
if (sequence.length % 3 === 0) {
|
|
118
|
+
const lastCodon = codons.pop() // Last codon is supposed to be a stop
|
|
119
|
+
if (lastCodon && !(lastCodon.toUpperCase() in STOP_CODONS)) {
|
|
120
|
+
checkResults.push({
|
|
121
|
+
_id: new ObjectID().toHexString(),
|
|
122
|
+
name: 'MissingStopCodonCheck',
|
|
123
|
+
ids,
|
|
124
|
+
refSeq: refSeq.toString(),
|
|
125
|
+
start: max,
|
|
126
|
+
end: max,
|
|
127
|
+
message: `Feature "${_id}" is missing a stop codon`,
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
188
131
|
checkResults.push({
|
|
189
132
|
_id: new ObjectID().toHexString(),
|
|
190
|
-
name: '
|
|
133
|
+
name: 'MultipleOfThreeCheck',
|
|
191
134
|
ids,
|
|
192
135
|
refSeq: refSeq.toString(),
|
|
193
|
-
start:
|
|
194
|
-
end,
|
|
195
|
-
message: `
|
|
136
|
+
start: min,
|
|
137
|
+
end: max,
|
|
138
|
+
message: `The coding sequence for feature "${_id}" is not a multiple of three`,
|
|
196
139
|
})
|
|
197
140
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
checkResults.push({
|
|
213
|
-
_id: new ObjectID().toHexString(),
|
|
214
|
-
name: 'InternalStopCodonCheck',
|
|
215
|
-
ids,
|
|
216
|
-
refSeq: refSeq.toString(),
|
|
217
|
-
start: codonStart,
|
|
218
|
-
end: codonEnd,
|
|
219
|
-
message: `The coding sequence for feature "${_id}" has an internal stop codon`,
|
|
220
|
-
})
|
|
141
|
+
for (const [idx, codon] of codons.entries()) {
|
|
142
|
+
const location = getOriginalCodonLocation(cdsLocation, strand, idx)
|
|
143
|
+
if (location && codon.toUpperCase() in STOP_CODONS) {
|
|
144
|
+
const [codonStart, codonEnd] = location
|
|
145
|
+
checkResults.push({
|
|
146
|
+
_id: new ObjectID().toHexString(),
|
|
147
|
+
name: 'InternalStopCodonCheck',
|
|
148
|
+
ids,
|
|
149
|
+
refSeq: refSeq.toString(),
|
|
150
|
+
start: codonStart,
|
|
151
|
+
end: codonEnd,
|
|
152
|
+
message: `The coding sequence for feature "${_id}" has an internal stop codon`,
|
|
153
|
+
})
|
|
154
|
+
}
|
|
221
155
|
}
|
|
222
156
|
}
|
|
223
157
|
return checkResults
|
|
224
158
|
}
|
|
225
159
|
|
|
160
|
+
function getCDSLocations(
|
|
161
|
+
feature: AnnotationFeatureSnapshot,
|
|
162
|
+
): CDSLocations | undefined {
|
|
163
|
+
if (feature.type !== 'mRNA') {
|
|
164
|
+
return
|
|
165
|
+
}
|
|
166
|
+
const { children, strand } = feature
|
|
167
|
+
if (!children) {
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
const cdsChildren = Object.values(children).filter(
|
|
171
|
+
(child) => child.type === 'CDS',
|
|
172
|
+
)
|
|
173
|
+
if (cdsChildren.length === 0) {
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
const cdsLocations: CDSLocations = []
|
|
177
|
+
for (const cds of cdsChildren) {
|
|
178
|
+
const { max: cdsMax, min: cdsMin } = cds
|
|
179
|
+
const locs: { min: number; max: number }[] = []
|
|
180
|
+
for (const child of Object.values(children)) {
|
|
181
|
+
if (child.type !== 'exon') {
|
|
182
|
+
continue
|
|
183
|
+
}
|
|
184
|
+
const [start, end] = intersection2(cdsMin, cdsMax, child.min, child.max)
|
|
185
|
+
if (start !== undefined && end !== undefined) {
|
|
186
|
+
locs.push({ min: start, max: end })
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
locs.sort(({ min: a }, { min: b }) => a - b)
|
|
190
|
+
if (strand === -1) {
|
|
191
|
+
locs.reverse()
|
|
192
|
+
}
|
|
193
|
+
let nextPhase: 0 | 1 | 2 = 0
|
|
194
|
+
const phasedLocs = locs.map((loc) => {
|
|
195
|
+
const phase = nextPhase
|
|
196
|
+
nextPhase = ((3 - ((loc.max - loc.min - phase + 3) % 3)) % 3) as 0 | 1 | 2
|
|
197
|
+
return { ...loc, phase }
|
|
198
|
+
})
|
|
199
|
+
cdsLocations.push(phasedLocs)
|
|
200
|
+
}
|
|
201
|
+
return cdsLocations
|
|
202
|
+
}
|
|
203
|
+
|
|
226
204
|
export class CDSCheck extends Check {
|
|
227
205
|
name = 'CDSCheck'
|
|
228
206
|
version = 1
|
|
@@ -232,44 +210,18 @@ export class CDSCheck extends Check {
|
|
|
232
210
|
feature: AnnotationFeatureSnapshot,
|
|
233
211
|
getSequence: (start: number, end: number) => Promise<string>,
|
|
234
212
|
): Promise<CheckResultSnapshot[]> {
|
|
235
|
-
if (feature.type === '
|
|
236
|
-
return
|
|
213
|
+
if (feature.type === 'mRNA') {
|
|
214
|
+
return checkMRNA(feature, getSequence)
|
|
237
215
|
}
|
|
238
216
|
|
|
239
217
|
if (!feature.children) {
|
|
240
218
|
return []
|
|
241
219
|
}
|
|
242
220
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
checkResults.push(...(await this.checkFeature(child, getSequence)))
|
|
247
|
-
}
|
|
248
|
-
return checkResults
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const cdsChildren = Object.values(feature.children).filter(
|
|
252
|
-
(child) => child.type === 'CDS',
|
|
253
|
-
)
|
|
254
|
-
if (cdsChildren.length === 0) {
|
|
255
|
-
throw new Error(`mRNA "${feature._id}" has no CDS children`)
|
|
256
|
-
}
|
|
257
|
-
const cdsChildrenWithDiscontinuousLocations = cdsChildren.filter(
|
|
258
|
-
(child) =>
|
|
259
|
-
child.discontinuousLocations && child.discontinuousLocations.length > 0,
|
|
260
|
-
)
|
|
261
|
-
if (cdsChildrenWithDiscontinuousLocations.length === 0) {
|
|
262
|
-
return checkCDS(cdsChildren, getSequence)
|
|
263
|
-
}
|
|
264
|
-
if (cdsChildrenWithDiscontinuousLocations.length === cdsChildren.length) {
|
|
265
|
-
const checkResults: CheckResultSnapshot[] = []
|
|
266
|
-
for (const child of cdsChildren) {
|
|
267
|
-
checkResults.push(...(await this.checkFeature(child, getSequence)))
|
|
268
|
-
}
|
|
269
|
-
return checkResults
|
|
221
|
+
const checkResults: CheckResultSnapshot[] = []
|
|
222
|
+
for (const child of Object.values(feature.children)) {
|
|
223
|
+
checkResults.push(...(await this.checkFeature(child, getSequence)))
|
|
270
224
|
}
|
|
271
|
-
|
|
272
|
-
`Mix of CDS with and without discontinuous locations found in mRNA "${feature._id}"`,
|
|
273
|
-
)
|
|
225
|
+
return checkResults
|
|
274
226
|
}
|
|
275
227
|
}
|
package/src/Common/jwtPayload.ts
CHANGED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-floating-promises */
|
|
2
|
+
import { describe, it } from 'node:test'
|
|
3
|
+
import { readFileSync } from 'node:fs'
|
|
4
|
+
|
|
5
|
+
import gff, { GFF3Feature } from '@gmod/gff'
|
|
6
|
+
import { assert, use } from 'chai'
|
|
7
|
+
import chaiExclude from 'chai-exclude'
|
|
8
|
+
|
|
9
|
+
import { gff3ToAnnotationFeature } from './gff3ToAnnotationFeature'
|
|
10
|
+
import { AnnotationFeatureSnapshot } from '@apollo-annotation/mst'
|
|
11
|
+
|
|
12
|
+
use(chaiExclude)
|
|
13
|
+
|
|
14
|
+
const testCases: [string, string, AnnotationFeatureSnapshot][] = [
|
|
15
|
+
[
|
|
16
|
+
'a feature with no children',
|
|
17
|
+
'ctgA example remark 1000 2000 . . . Name=Remark:hga;Alias=hga\n',
|
|
18
|
+
{
|
|
19
|
+
_id: '66c51f3e002c683eaf98a223',
|
|
20
|
+
refSeq: 'ctgA',
|
|
21
|
+
type: 'remark',
|
|
22
|
+
min: 999,
|
|
23
|
+
max: 2000,
|
|
24
|
+
attributes: {
|
|
25
|
+
gff_source: ['example'],
|
|
26
|
+
gff_name: ['Remark:hga'],
|
|
27
|
+
gff_alias: ['hga'],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
[
|
|
32
|
+
'a feature with two children',
|
|
33
|
+
`ctgA est EST_match 1050 3202 . + . ID=Match1;Name=agt830.5;Target=agt830.5 1 654
|
|
34
|
+
ctgA est match_part 1050 1500 . + . Parent=Match1;Name=agt830.5;Target=agt830.5 1 451
|
|
35
|
+
ctgA est match_part 3000 3202 . + . Parent=Match1;Name=agt830.5;Target=agt830.5 452 654
|
|
36
|
+
`,
|
|
37
|
+
{
|
|
38
|
+
_id: '66cf9fbb4e947fa2c27d3d6a',
|
|
39
|
+
refSeq: 'ctgA',
|
|
40
|
+
type: 'EST_match',
|
|
41
|
+
min: 1049,
|
|
42
|
+
max: 3202,
|
|
43
|
+
strand: 1,
|
|
44
|
+
children: {
|
|
45
|
+
'66cf9fbb4e947fa2c27d3d68': {
|
|
46
|
+
_id: '66cf9fbb4e947fa2c27d3d68',
|
|
47
|
+
refSeq: 'ctgA',
|
|
48
|
+
type: 'match_part',
|
|
49
|
+
min: 1049,
|
|
50
|
+
max: 1500,
|
|
51
|
+
strand: 1,
|
|
52
|
+
attributes: {
|
|
53
|
+
gff_source: ['est'],
|
|
54
|
+
gff_name: ['agt830.5'],
|
|
55
|
+
gff_target: ['agt830.5 1 451'],
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
'66cf9fbb4e947fa2c27d3d69': {
|
|
59
|
+
_id: '66cf9fbb4e947fa2c27d3d69',
|
|
60
|
+
refSeq: 'ctgA',
|
|
61
|
+
type: 'match_part',
|
|
62
|
+
min: 2999,
|
|
63
|
+
max: 3202,
|
|
64
|
+
strand: 1,
|
|
65
|
+
attributes: {
|
|
66
|
+
gff_source: ['est'],
|
|
67
|
+
gff_name: ['agt830.5'],
|
|
68
|
+
gff_target: ['agt830.5 452 654'],
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
attributes: {
|
|
73
|
+
gff_source: ['est'],
|
|
74
|
+
gff_id: ['Match1'],
|
|
75
|
+
gff_name: ['agt830.5'],
|
|
76
|
+
gff_target: ['agt830.5 1 654'],
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
interface AnnotationFeatureSnapshotWithChildrenArray
|
|
83
|
+
extends Omit<AnnotationFeatureSnapshot, 'children'> {
|
|
84
|
+
children?: AnnotationFeatureSnapshotWithChildrenArray[]
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function childrenToArray(
|
|
88
|
+
feature: AnnotationFeatureSnapshot,
|
|
89
|
+
): AnnotationFeatureSnapshotWithChildrenArray {
|
|
90
|
+
const { children } = feature
|
|
91
|
+
if (!children) {
|
|
92
|
+
return feature as AnnotationFeatureSnapshotWithChildrenArray
|
|
93
|
+
}
|
|
94
|
+
const childrenArray = Object.values(children).map((child) =>
|
|
95
|
+
childrenToArray(child),
|
|
96
|
+
)
|
|
97
|
+
return { ...feature, children: childrenArray }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function compareFeatures(
|
|
101
|
+
feature1: AnnotationFeatureSnapshot,
|
|
102
|
+
feature2: AnnotationFeatureSnapshot,
|
|
103
|
+
) {
|
|
104
|
+
assert.deepEqualExcludingEvery(
|
|
105
|
+
childrenToArray(feature1),
|
|
106
|
+
childrenToArray(feature2),
|
|
107
|
+
'_id',
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function readFeatureFile(fn: string): GFF3Feature[] {
|
|
112
|
+
const lines = readFileSync(fn).toString().split('\n')
|
|
113
|
+
const feature: string[] = []
|
|
114
|
+
for (const line of lines) {
|
|
115
|
+
if (!line.startsWith('#')) {
|
|
116
|
+
feature.push(line)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const inGff = gff.parseStringSync(feature.join('\n')) as GFF3Feature[]
|
|
120
|
+
return inGff
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function readAnnotationFeatureSnapshot(fn: string): AnnotationFeatureSnapshot {
|
|
124
|
+
const lines = readFileSync(fn).toString()
|
|
125
|
+
return JSON.parse(lines) as AnnotationFeatureSnapshot
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const [ex1, ex2, ex3, ex4] = readFeatureFile(
|
|
129
|
+
'test_data/gene_representations.gff3',
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
describe('gff3ToAnnotationFeature examples', () => {
|
|
133
|
+
it('Convert one CDS', () => {
|
|
134
|
+
const actual = gff3ToAnnotationFeature(
|
|
135
|
+
readFeatureFile('test_data/one_cds.gff3')[0],
|
|
136
|
+
)
|
|
137
|
+
const expected = readAnnotationFeatureSnapshot('test_data/one_cds.json')
|
|
138
|
+
compareFeatures(actual, expected)
|
|
139
|
+
})
|
|
140
|
+
it('Convert two CDSs', () => {
|
|
141
|
+
const actual = gff3ToAnnotationFeature(
|
|
142
|
+
readFeatureFile('test_data/two_cds.gff3')[0],
|
|
143
|
+
)
|
|
144
|
+
const expected = readAnnotationFeatureSnapshot('test_data/two_cds.json')
|
|
145
|
+
compareFeatures(actual, expected)
|
|
146
|
+
})
|
|
147
|
+
it('Convert example 1', () => {
|
|
148
|
+
const actual = gff3ToAnnotationFeature(ex1)
|
|
149
|
+
const txt = JSON.stringify(actual, null, 2)
|
|
150
|
+
|
|
151
|
+
assert.equal(txt.match(/"type": "CDS"/g)?.length, 4)
|
|
152
|
+
assert.equal(txt.match(/"type": "TF_binding_site"/g)?.length, 1)
|
|
153
|
+
|
|
154
|
+
const expected = readAnnotationFeatureSnapshot('test_data/example01.json')
|
|
155
|
+
compareFeatures(actual, expected)
|
|
156
|
+
})
|
|
157
|
+
it('Convert example 2', () => {
|
|
158
|
+
const actual = gff3ToAnnotationFeature(ex2)
|
|
159
|
+
const txt = JSON.stringify(actual, null, 2)
|
|
160
|
+
assert.equal(txt.match(/"type": "CDS"/g)?.length, 4)
|
|
161
|
+
const expected = readAnnotationFeatureSnapshot('test_data/example02.json')
|
|
162
|
+
compareFeatures(actual, expected)
|
|
163
|
+
})
|
|
164
|
+
it('Convert example 3', () => {
|
|
165
|
+
// NB: In example 3 (and in the other examples) mRNA10003 produces two proteins.
|
|
166
|
+
// In the other examples the two proteins are identified by sharing the same cds id.
|
|
167
|
+
// In example 3 instead each cds has a unique id so the two proteins are identified by the order they
|
|
168
|
+
// appear in the gff.
|
|
169
|
+
const actual = gff3ToAnnotationFeature(ex3)
|
|
170
|
+
const txt = JSON.stringify(actual, null, 2)
|
|
171
|
+
assert.equal(txt.match(/"type": "CDS"/g)?.length, 4)
|
|
172
|
+
|
|
173
|
+
// const expected = readAnnotationFeatureSnapshot('test_data/example03.json')
|
|
174
|
+
// compareFeatures(actual, expected)
|
|
175
|
+
})
|
|
176
|
+
it('Convert example 4', () => {
|
|
177
|
+
const ft = JSON.stringify(ex4, null, 2)
|
|
178
|
+
assert.equal(ft.match(/"type": "five_prime_UTR"/g)?.length, 6)
|
|
179
|
+
assert.equal(ft.match(/"type": "three_prime_UTR"/g)?.length, 3)
|
|
180
|
+
|
|
181
|
+
const actual = gff3ToAnnotationFeature(ex4)
|
|
182
|
+
const txt = JSON.stringify(actual, null, 2)
|
|
183
|
+
assert.equal(txt.match(/"type": "CDS"/g)?.length, 4)
|
|
184
|
+
assert.equal(txt.match(/prime_UTR/g), null)
|
|
185
|
+
|
|
186
|
+
const expected = readAnnotationFeatureSnapshot('test_data/example04.json')
|
|
187
|
+
compareFeatures(actual, expected)
|
|
188
|
+
})
|
|
189
|
+
it('Convert braker gff', () => {
|
|
190
|
+
const [gffFeature] = readFeatureFile('test_data/braker.gff')
|
|
191
|
+
const actual = gff3ToAnnotationFeature(gffFeature)
|
|
192
|
+
const txt = JSON.stringify(actual, null, 2)
|
|
193
|
+
assert.equal(txt.match(/intron/g), null)
|
|
194
|
+
assert.equal(txt.match(/_codon/g), null)
|
|
195
|
+
})
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
describe('gff3ToAnnotationFeature', () => {
|
|
199
|
+
for (const testCase of testCases) {
|
|
200
|
+
const [description, featureLine, convertedFeature] = testCase
|
|
201
|
+
it(`converts ${description}`, () => {
|
|
202
|
+
const gff3Feature = gff.parseStringSync(featureLine, {
|
|
203
|
+
parseSequences: false,
|
|
204
|
+
})
|
|
205
|
+
const feature = gff3ToAnnotationFeature(gff3Feature[0])
|
|
206
|
+
compareFeatures(convertedFeature, feature)
|
|
207
|
+
})
|
|
208
|
+
}
|
|
209
|
+
})
|