@apollo-annotation/shared 0.3.7 → 0.3.8
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.js +1 -1
- package/dist/Changes/AddAssemblyAndFeaturesFromFileChange.js.map +1 -1
- package/dist/Changes/AddAssemblyFromFileChange.js +1 -1
- package/dist/Changes/AddAssemblyFromFileChange.js.map +1 -1
- package/dist/Changes/AddRefSeqAliasesChange.js +2 -4
- package/dist/Changes/AddRefSeqAliasesChange.js.map +1 -1
- package/dist/Changes/ImportJBrowseConfigChange.js +1 -1
- package/dist/Changes/ImportJBrowseConfigChange.js.map +1 -1
- package/dist/Changes/MergeExonsChange.d.ts +0 -1
- package/dist/Changes/MergeExonsChange.js +14 -30
- package/dist/Changes/MergeExonsChange.js.map +1 -1
- package/dist/Changes/MergeTranscriptsChange.d.ts +1 -3
- package/dist/Changes/MergeTranscriptsChange.js +55 -56
- package/dist/Changes/MergeTranscriptsChange.js.map +1 -1
- package/dist/Checks/CDSCheck.d.ts +1 -1
- package/dist/Checks/CDSCheck.js +8 -9
- package/dist/Checks/CDSCheck.js.map +1 -1
- package/dist/Checks/TranscriptCheck.d.ts +9 -0
- package/dist/Checks/TranscriptCheck.js +109 -0
- package/dist/Checks/TranscriptCheck.js.map +1 -0
- package/dist/Checks/index.d.ts +1 -0
- package/dist/Checks/index.js +1 -0
- package/dist/Checks/index.js.map +1 -1
- package/dist/GFF3/gff3ToAnnotationFeature.test.js +1 -1
- package/dist/GFF3/gff3ToAnnotationFeature.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/util.d.ts +3 -0
- package/dist/util.js +33 -0
- package/dist/util.js.map +1 -1
- package/package.json +7 -7
- package/src/Changes/AddAssemblyAndFeaturesFromFileChange.ts +2 -1
- package/src/Changes/AddAssemblyFromFileChange.ts +1 -1
- package/src/Changes/AddRefSeqAliasesChange.ts +2 -4
- package/src/Changes/ImportJBrowseConfigChange.ts +1 -1
- package/src/Changes/MergeExonsChange.ts +52 -36
- package/src/Changes/MergeTranscriptsChange.ts +82 -67
- package/src/Checks/CDSCheck.ts +10 -10
- package/src/Checks/TranscriptCheck.ts +139 -0
- package/src/Checks/index.ts +1 -0
- package/src/GFF3/gff3ToAnnotationFeature.test.ts +1 -1
- package/src/util.ts +37 -0
- package/test_data/example01.json +20 -20
- package/test_data/example02.json +38 -38
- package/test_data/example04.json +57 -57
- package/test_data/gene_representations.gff3 +1054 -276
package/dist/util.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
import { type Feature } from '@apollo-annotation/schemas';
|
|
2
|
+
import { type IKeyValueMap } from 'mobx';
|
|
2
3
|
export declare function splitStringIntoChunks(input: string, chunkSize: number): string[];
|
|
3
4
|
export declare function getPrintableId(feature: Feature): string;
|
|
5
|
+
export declare function attributesToRecords(attributes: IKeyValueMap<readonly string[] | undefined> | undefined): Record<string, string[] | undefined>;
|
|
6
|
+
export declare function stringifyAttributes(attributes: Record<string, string[] | undefined> | undefined): string;
|
package/dist/util.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.splitStringIntoChunks = splitStringIntoChunks;
|
|
4
4
|
exports.getPrintableId = getPrintableId;
|
|
5
|
+
exports.attributesToRecords = attributesToRecords;
|
|
6
|
+
exports.stringifyAttributes = stringifyAttributes;
|
|
5
7
|
function splitStringIntoChunks(input, chunkSize) {
|
|
6
8
|
const chunks = [];
|
|
7
9
|
for (let i = 0; i < input.length; i += chunkSize) {
|
|
@@ -22,4 +24,35 @@ function getPrintableId(feature) {
|
|
|
22
24
|
}
|
|
23
25
|
return `_id: ${feature._id.toString()}`;
|
|
24
26
|
}
|
|
27
|
+
function attributesToRecords(attributes) {
|
|
28
|
+
const records = {};
|
|
29
|
+
if (!attributes) {
|
|
30
|
+
return records;
|
|
31
|
+
}
|
|
32
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
33
|
+
records[key] = value?.slice();
|
|
34
|
+
}
|
|
35
|
+
return records;
|
|
36
|
+
}
|
|
37
|
+
function stringifyAttributes(attributes) {
|
|
38
|
+
if (!attributes) {
|
|
39
|
+
return '';
|
|
40
|
+
}
|
|
41
|
+
const str = [];
|
|
42
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
43
|
+
let attributeName = key;
|
|
44
|
+
if (attributeName.startsWith('gff_')) {
|
|
45
|
+
attributeName = attributeName.slice(4);
|
|
46
|
+
attributeName =
|
|
47
|
+
attributeName.charAt(0).toUpperCase() + attributeName.slice(1);
|
|
48
|
+
}
|
|
49
|
+
if (value) {
|
|
50
|
+
str.push(`${attributeName}=${value.join(',')}`);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
str.push(attributeName);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return encodeURIComponent(str.join(';'));
|
|
57
|
+
}
|
|
25
58
|
//# sourceMappingURL=util.js.map
|
package/dist/util.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":";;AAIA,sDAUC;AAED,wCAWC;AAED,kDAWC;AAED,kDAqBC;AA3DD,SAAgB,qBAAqB,CACnC,KAAa,EACb,SAAiB;IAEjB,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,SAAS,EAAE,CAAC;QACjD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,CAAA;QAC3C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACpB,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAgB,cAAc,CAAC,OAAgB;IAC7C,MAAM,EAAE,GAAG,OAAuC,CAAA;IAClD,MAAM,MAAM,GAAG,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;IACrD,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,MAAM,MAAM,UAAU,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAA;IACxD,CAAC;IACD,MAAM,QAAQ,GAAG,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;IACzD,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,QAAQ,QAAQ,UAAU,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAA;IAC5D,CAAC;IACD,OAAO,QAAQ,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAA;AACzC,CAAC;AAED,SAAgB,mBAAmB,CACjC,UAAmE;IAEnE,MAAM,OAAO,GAAyC,EAAE,CAAA;IACxD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,OAAO,CAAA;IAChB,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QACtD,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,EAAE,KAAK,EAAE,CAAA;IAC/B,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,SAAgB,mBAAmB,CACjC,UAA4D;IAE5D,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,EAAE,CAAA;IACX,CAAC;IACD,MAAM,GAAG,GAAG,EAAE,CAAA;IACd,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QACtD,IAAI,aAAa,GAAG,GAAG,CAAA;QACvB,IAAI,aAAa,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACrC,aAAa,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;YACtC,aAAa;gBACX,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QAClE,CAAC;QACD,IAAI,KAAK,EAAE,CAAC;YACV,GAAG,CAAC,IAAI,CAAC,GAAG,aAAa,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACjD,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;QACzB,CAAC;IACH,CAAC;IACD,OAAO,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AAC1C,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apollo-annotation/shared",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "yarn clean && tsc --build",
|
|
@@ -10,12 +10,12 @@
|
|
|
10
10
|
"test:ci": "NODE_V8_COVERAGE=./coverage glob -c \"tsx --test --test-reporter spec --experimental-test-coverage \" \"**/*.test.ts\""
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@apollo-annotation/common": "^0.3.
|
|
14
|
-
"@apollo-annotation/mst": "^0.3.
|
|
15
|
-
"@apollo-annotation/schemas": "^0.3.
|
|
13
|
+
"@apollo-annotation/common": "^0.3.8",
|
|
14
|
+
"@apollo-annotation/mst": "^0.3.8",
|
|
15
|
+
"@apollo-annotation/schemas": "^0.3.8",
|
|
16
16
|
"@gmod/gff": "1.2.0",
|
|
17
17
|
"@gmod/indexedfasta": "^2.0.4",
|
|
18
|
-
"@jbrowse/core": "^3.
|
|
18
|
+
"@jbrowse/core": "^3.6.5",
|
|
19
19
|
"bson-objectid": "^2.0.4",
|
|
20
20
|
"generic-filehandle": "^3.0.0",
|
|
21
21
|
"jwt-decode": "^3.1.2",
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"typescript": "^5.5.3"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@mui/material": "^
|
|
43
|
-
"@mui/x-data-grid": "^
|
|
42
|
+
"@mui/material": "^7.0.0",
|
|
43
|
+
"@mui/x-data-grid": "^8.0.0",
|
|
44
44
|
"mobx": "^6.6.1",
|
|
45
45
|
"mobx-react": "^7.2.1",
|
|
46
46
|
"mobx-state-tree": "^5.4.0",
|
|
@@ -90,7 +90,8 @@ export class AddAssemblyAndFeaturesFromFileChange extends FromFileBaseChange {
|
|
|
90
90
|
throw new Error(`Assembly "${assemblyName}" already exists`)
|
|
91
91
|
}
|
|
92
92
|
// get checks
|
|
93
|
-
const checkDocs = await checkModel.find({
|
|
93
|
+
const checkDocs = await checkModel.find({ isDefault: true }).exec()
|
|
94
|
+
|
|
94
95
|
const checks = checkDocs.map((checkDoc) => checkDoc._id.toHexString())
|
|
95
96
|
// Add assembly
|
|
96
97
|
const [newAssemblyDoc] = await assemblyModel.create([
|
|
@@ -128,7 +128,7 @@ export class AddAssemblyFromFileChange extends FromFileBaseChange {
|
|
|
128
128
|
if (assemblyDoc) {
|
|
129
129
|
throw new Error(`Assembly "${assemblyName}" already exists`)
|
|
130
130
|
}
|
|
131
|
-
const checkDocs = await checkModel.find({
|
|
131
|
+
const checkDocs = await checkModel.find({ isDefault: true }).exec()
|
|
132
132
|
const checks = checkDocs.map((checkDoc) => checkDoc._id.toHexString())
|
|
133
133
|
const [newAssemblyDoc] = await assemblyModel.create([
|
|
134
134
|
{
|
|
@@ -36,9 +36,8 @@ export class AddRefSeqAliasesChange extends AssemblySpecificChange {
|
|
|
36
36
|
throw new Error(`assembly ${this.assembly} not found`)
|
|
37
37
|
}
|
|
38
38
|
const sessionAliases = assembly.refNameAliases
|
|
39
|
-
const sessionLCAliases = assembly.lowerCaseRefNameAliases
|
|
40
39
|
|
|
41
|
-
if (!sessionAliases
|
|
40
|
+
if (!sessionAliases) {
|
|
42
41
|
throw new Error('Session refNameAliases not found in assembly')
|
|
43
42
|
}
|
|
44
43
|
|
|
@@ -46,10 +45,9 @@ export class AddRefSeqAliasesChange extends AssemblySpecificChange {
|
|
|
46
45
|
const { aliases, refName } = refSeqAlias
|
|
47
46
|
for (const alias of aliases) {
|
|
48
47
|
sessionAliases[alias] = refName
|
|
49
|
-
sessionLCAliases[alias.toLowerCase()] = refName
|
|
50
48
|
}
|
|
51
49
|
}
|
|
52
|
-
assembly.setRefNameAliases(sessionAliases
|
|
50
|
+
assembly.setRefNameAliases(sessionAliases)
|
|
53
51
|
return Promise.resolve()
|
|
54
52
|
}
|
|
55
53
|
|
|
@@ -75,7 +75,7 @@ export function filterJBrowseConfig(config: JBrowseConfig): JBrowseConfig {
|
|
|
75
75
|
filteredConfig.plugins = plugins.filter((p) => p.name !== 'Apollo')
|
|
76
76
|
}
|
|
77
77
|
if (tracks) {
|
|
78
|
-
filteredConfig.
|
|
78
|
+
filteredConfig.tracks = tracks.filter((t) => t.type !== 'ApolloTrack')
|
|
79
79
|
}
|
|
80
80
|
return filteredConfig
|
|
81
81
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable unicorn/prefer-structured-clone */
|
|
1
2
|
/* eslint-disable @typescript-eslint/require-await */
|
|
2
3
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
3
4
|
|
|
@@ -11,6 +12,8 @@ import {
|
|
|
11
12
|
} from '@apollo-annotation/common'
|
|
12
13
|
import { type AnnotationFeatureSnapshot } from '@apollo-annotation/mst'
|
|
13
14
|
|
|
15
|
+
import { attributesToRecords, stringifyAttributes } from '../util'
|
|
16
|
+
|
|
14
17
|
import { findAndDeleteChildFeature } from './DeleteFeatureChange'
|
|
15
18
|
import { UndoMergeExonsChange } from './UndoMergeExonsChange'
|
|
16
19
|
|
|
@@ -89,7 +92,15 @@ export class MergeExonsChange extends FeatureChange {
|
|
|
89
92
|
}
|
|
90
93
|
mergedExon.min = Math.min(firstExon.min, secondExon.min)
|
|
91
94
|
mergedExon.max = Math.max(firstExon.max, secondExon.max)
|
|
92
|
-
|
|
95
|
+
|
|
96
|
+
const mergedAttributes: Record<string, string[]> = mergedExon.attributes
|
|
97
|
+
? JSON.parse(JSON.stringify(mergedExon.attributes))
|
|
98
|
+
: {}
|
|
99
|
+
mergedAttributes.merged_with = [
|
|
100
|
+
stringifyAttributes(attributesToRecords(secondExon.attributes)),
|
|
101
|
+
]
|
|
102
|
+
mergedExon.attributes = mergedAttributes
|
|
103
|
+
|
|
93
104
|
const deletedIds = findAndDeleteChildFeature(
|
|
94
105
|
topLevelFeature,
|
|
95
106
|
secondExon._id,
|
|
@@ -123,11 +134,16 @@ export class MergeExonsChange extends FeatureChange {
|
|
|
123
134
|
}
|
|
124
135
|
mergedExon.setMin(Math.min(firstExon.min, secondExon.min))
|
|
125
136
|
mergedExon.setMax(Math.max(firstExon.max, secondExon.max))
|
|
126
|
-
|
|
127
|
-
mergedExon.
|
|
128
|
-
|
|
129
|
-
|
|
137
|
+
|
|
138
|
+
const mrg = mergedExon.attributes.get('merged_with')?.slice() ?? []
|
|
139
|
+
const mergedWith = stringifyAttributes(
|
|
140
|
+
attributesToRecords(secondExon.attributes),
|
|
141
|
+
)
|
|
142
|
+
if (!mrg.includes(mergedWith)) {
|
|
143
|
+
mrg.push(mergedWith)
|
|
130
144
|
}
|
|
145
|
+
mergedExon.setAttribute('merged_with', mrg)
|
|
146
|
+
|
|
131
147
|
mergedExon.parent?.deleteChild(secondExon._id)
|
|
132
148
|
}
|
|
133
149
|
}
|
|
@@ -151,35 +167,35 @@ export class MergeExonsChange extends FeatureChange {
|
|
|
151
167
|
)
|
|
152
168
|
}
|
|
153
169
|
|
|
154
|
-
mergeAttributes(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
): Record<string, string[]> {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
170
|
+
// mergeAttributes(
|
|
171
|
+
// firstExon: AnnotationFeatureSnapshot,
|
|
172
|
+
// secondExon: AnnotationFeatureSnapshot,
|
|
173
|
+
// ): Record<string, string[]> {
|
|
174
|
+
// let mergedAttrs: Record<string, string[]> = {}
|
|
175
|
+
// if (firstExon.attributes) {
|
|
176
|
+
// // eslint-disable-next-line unicorn/prefer-structured-clone
|
|
177
|
+
// mergedAttrs = JSON.parse(JSON.stringify(firstExon.attributes))
|
|
178
|
+
// }
|
|
179
|
+
|
|
180
|
+
// if (secondExon.attributes) {
|
|
181
|
+
// // eslint-disable-next-line unicorn/prefer-structured-clone
|
|
182
|
+
// const attrs: Record<string, string[]> = JSON.parse(
|
|
183
|
+
// JSON.stringify(secondExon.attributes),
|
|
184
|
+
// )
|
|
185
|
+
// for (const key of Object.keys(attrs)) {
|
|
186
|
+
// if (key === '_id' || key === 'gff_id') {
|
|
187
|
+
// continue
|
|
188
|
+
// }
|
|
189
|
+
// if (!Object.keys(mergedAttrs).includes(key)) {
|
|
190
|
+
// mergedAttrs[key] = []
|
|
191
|
+
// }
|
|
192
|
+
// attrs[key].map((x) => {
|
|
193
|
+
// if (!mergedAttrs[key].includes(x)) {
|
|
194
|
+
// mergedAttrs[key].push(x)
|
|
195
|
+
// }
|
|
196
|
+
// })
|
|
197
|
+
// }
|
|
198
|
+
// }
|
|
199
|
+
// return mergedAttrs
|
|
200
|
+
// }
|
|
185
201
|
}
|
|
@@ -16,6 +16,9 @@ import {
|
|
|
16
16
|
} from '@apollo-annotation/mst'
|
|
17
17
|
import { type Feature } from '@apollo-annotation/schemas'
|
|
18
18
|
import { doesIntersect2 } from '@jbrowse/core/util'
|
|
19
|
+
import { getSnapshot } from 'mobx-state-tree'
|
|
20
|
+
|
|
21
|
+
import { attributesToRecords, stringifyAttributes } from '../util'
|
|
19
22
|
|
|
20
23
|
import { findAndDeleteChildFeature } from './DeleteFeatureChange'
|
|
21
24
|
import { UndoMergeTranscriptsChange } from './UndoMergeTranscriptsChange'
|
|
@@ -42,6 +45,7 @@ interface SerializedMergeTranscriptsChangeMultiple
|
|
|
42
45
|
export type SerializedMergeTranscriptsChange =
|
|
43
46
|
| SerializedMergeTranscriptsChangeSingle
|
|
44
47
|
| SerializedMergeTranscriptsChangeMultiple
|
|
48
|
+
|
|
45
49
|
export class MergeTranscriptsChange extends FeatureChange {
|
|
46
50
|
typeName = 'MergeTranscriptsChange' as const
|
|
47
51
|
changes: MergeTranscriptsChangeDetails[]
|
|
@@ -50,6 +54,10 @@ export class MergeTranscriptsChange extends FeatureChange {
|
|
|
50
54
|
super(json, options)
|
|
51
55
|
this.changes = 'changes' in json ? json.changes : [json]
|
|
52
56
|
}
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/class-literal-property-style
|
|
58
|
+
get notification() {
|
|
59
|
+
return 'Transcripts successfully merged'
|
|
60
|
+
}
|
|
53
61
|
|
|
54
62
|
toJSON(): SerializedMergeTranscriptsChange {
|
|
55
63
|
const { assembly, changedIds, changes, typeName } = this
|
|
@@ -111,7 +119,21 @@ export class MergeTranscriptsChange extends FeatureChange {
|
|
|
111
119
|
) {
|
|
112
120
|
firstTranscript.min = Math.min(firstTranscript.min, secondTranscript.min)
|
|
113
121
|
firstTranscript.max = Math.max(firstTranscript.max, secondTranscript.max)
|
|
114
|
-
|
|
122
|
+
|
|
123
|
+
const mergedAttributes: Record<string, string[]> =
|
|
124
|
+
firstTranscript.attributes
|
|
125
|
+
? JSON.parse(JSON.stringify(firstTranscript.attributes))
|
|
126
|
+
: {}
|
|
127
|
+
if (secondTranscript.attributes) {
|
|
128
|
+
if (!Object.keys(mergedAttributes).includes('merged_with')) {
|
|
129
|
+
mergedAttributes.merged_with = []
|
|
130
|
+
}
|
|
131
|
+
mergedAttributes.merged_with.push(
|
|
132
|
+
stringifyAttributes(attributesToRecords(secondTranscript.attributes)),
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
firstTranscript.attributes = mergedAttributes
|
|
136
|
+
|
|
115
137
|
if (secondTranscript.children) {
|
|
116
138
|
for (const [, secondFeatureChild] of Object.entries(
|
|
117
139
|
secondTranscript.children,
|
|
@@ -133,9 +155,13 @@ export class MergeTranscriptsChange extends FeatureChange {
|
|
|
133
155
|
}
|
|
134
156
|
let merged = false
|
|
135
157
|
let mrgChild: Feature | undefined
|
|
136
|
-
|
|
158
|
+
let toDelete
|
|
159
|
+
for (const [, firstFeatureChild] of firstTranscript.children) {
|
|
137
160
|
if (!merged || !mrgChild) {
|
|
161
|
+
toDelete = false
|
|
138
162
|
mrgChild = firstFeatureChild
|
|
163
|
+
} else {
|
|
164
|
+
toDelete = true
|
|
139
165
|
}
|
|
140
166
|
if (
|
|
141
167
|
mrgChild.type === secondFeatureChild.type &&
|
|
@@ -163,9 +189,36 @@ export class MergeTranscriptsChange extends FeatureChange {
|
|
|
163
189
|
mrgChild.max,
|
|
164
190
|
firstFeatureChild.max,
|
|
165
191
|
)
|
|
166
|
-
|
|
167
|
-
mrgChild.attributes
|
|
168
|
-
|
|
192
|
+
|
|
193
|
+
if (!mrgChild.attributes) {
|
|
194
|
+
mrgChild.attributes = {}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const mrgChildAttr: Record<string, string[]> = JSON.parse(
|
|
198
|
+
JSON.stringify(mrgChild.attributes),
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
if (!Object.keys(mrgChildAttr).includes('merged_with')) {
|
|
202
|
+
mrgChildAttr.merged_with = []
|
|
203
|
+
}
|
|
204
|
+
const mergedWithAttributes = mrgChildAttr.merged_with
|
|
205
|
+
mergedWithAttributes.push(
|
|
206
|
+
stringifyAttributes(
|
|
207
|
+
attributesToRecords(secondFeatureChild.attributes),
|
|
208
|
+
),
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
if (toDelete) {
|
|
212
|
+
const recs: Record<string, string[] | undefined> =
|
|
213
|
+
firstFeatureChild.attributes
|
|
214
|
+
? JSON.parse(JSON.stringify(firstFeatureChild.attributes))
|
|
215
|
+
: undefined
|
|
216
|
+
mergedWithAttributes.push(stringifyAttributes(recs))
|
|
217
|
+
firstTranscript.children.delete(firstFeatureChild._id.toString())
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
mrgChildAttr.merged_with = [...new Set(mergedWithAttributes)]
|
|
221
|
+
mrgChild.attributes = mrgChildAttr
|
|
169
222
|
merged = true
|
|
170
223
|
}
|
|
171
224
|
}
|
|
@@ -215,7 +268,16 @@ export class MergeTranscriptsChange extends FeatureChange {
|
|
|
215
268
|
firstTranscript.setMin(Math.min(firstTranscript.min, secondTranscript.min))
|
|
216
269
|
firstTranscript.setMax(Math.max(firstTranscript.max, secondTranscript.max))
|
|
217
270
|
|
|
218
|
-
|
|
271
|
+
const mrg = firstTranscript.attributes.get('merged_with')?.slice() ?? []
|
|
272
|
+
const mergedWith = stringifyAttributes(
|
|
273
|
+
attributesToRecords(secondTranscript.attributes),
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
if (!mrg.includes(mergedWith)) {
|
|
277
|
+
// executeOnClient runs twice (?!) so avoid adding this key again
|
|
278
|
+
mrg.push(mergedWith)
|
|
279
|
+
}
|
|
280
|
+
firstTranscript.setAttribute('merged_with', mrg)
|
|
219
281
|
|
|
220
282
|
if (secondTranscript.children) {
|
|
221
283
|
for (const [, secondFeatureChild] of Object.entries(
|
|
@@ -269,15 +331,20 @@ export class MergeTranscriptsChange extends FeatureChange {
|
|
|
269
331
|
Math.max(secondFeatureChild.max, mrgChild.max, firstFeatureChild.max),
|
|
270
332
|
)
|
|
271
333
|
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
334
|
+
const mergedWithAttributes =
|
|
335
|
+
mrgChild.attributes.get('merged_with')?.slice() ?? []
|
|
336
|
+
mergedWithAttributes.push(
|
|
337
|
+
stringifyAttributes(
|
|
338
|
+
attributesToRecords(secondFeatureChild.attributes),
|
|
339
|
+
),
|
|
340
|
+
)
|
|
278
341
|
if (toDelete) {
|
|
342
|
+
mergedWithAttributes.push(
|
|
343
|
+
stringifyAttributes(getSnapshot(firstFeatureChild).attributes),
|
|
344
|
+
)
|
|
279
345
|
firstTranscript.deleteChild(firstFeatureChild._id)
|
|
280
346
|
}
|
|
347
|
+
mrgChild.setAttribute('merged_with', [...new Set(mergedWithAttributes)])
|
|
281
348
|
merged = true
|
|
282
349
|
}
|
|
283
350
|
}
|
|
@@ -288,71 +355,19 @@ export class MergeTranscriptsChange extends FeatureChange {
|
|
|
288
355
|
})
|
|
289
356
|
}
|
|
290
357
|
|
|
291
|
-
if (
|
|
358
|
+
if (merged && mrgChild) {
|
|
359
|
+
firstTranscript.addChild(getSnapshot(mrgChild))
|
|
360
|
+
} else {
|
|
292
361
|
// This secondFeatureChild has no overlap with any feature in the
|
|
293
362
|
// receiving transcript so we add it as it is to the receiving transcript
|
|
294
363
|
firstTranscript.addChild(secondFeatureChild)
|
|
295
364
|
}
|
|
296
365
|
}
|
|
297
366
|
|
|
298
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
299
|
-
isAnnotationFeature(obj: any): obj is AnnotationFeature {
|
|
300
|
-
return (
|
|
301
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
302
|
-
typeof obj.setMin === 'function' &&
|
|
303
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
304
|
-
typeof obj.setMax === 'function' &&
|
|
305
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
306
|
-
typeof obj.addChild === 'function' &&
|
|
307
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
308
|
-
typeof obj.deleteChild === 'function'
|
|
309
|
-
)
|
|
310
|
-
}
|
|
311
|
-
|
|
312
367
|
async executeOnLocalGFF3(_backend: LocalGFF3DataStore) {
|
|
313
368
|
throw new Error('executeOnLocalGFF3 not implemented')
|
|
314
369
|
}
|
|
315
370
|
|
|
316
|
-
/* Merge attributes from source into destination */
|
|
317
|
-
mergeAttributes(
|
|
318
|
-
destination: Feature | AnnotationFeature,
|
|
319
|
-
source: AnnotationFeatureSnapshot,
|
|
320
|
-
): Record<string, string[]> {
|
|
321
|
-
const destAttrs: Record<string, string[]> = destination.attributes
|
|
322
|
-
? JSON.parse(JSON.stringify(destination.attributes))
|
|
323
|
-
: {}
|
|
324
|
-
if (source.attributes) {
|
|
325
|
-
const sourceAttrs: Record<string, string[]> = JSON.parse(
|
|
326
|
-
JSON.stringify(source.attributes),
|
|
327
|
-
)
|
|
328
|
-
Object.entries(sourceAttrs).map(([key, value]) => {
|
|
329
|
-
if (!(key in destAttrs)) {
|
|
330
|
-
destAttrs[key] = []
|
|
331
|
-
}
|
|
332
|
-
value.map((x) => {
|
|
333
|
-
if (!destAttrs[key].includes(x)) {
|
|
334
|
-
destAttrs[key].push(x)
|
|
335
|
-
}
|
|
336
|
-
})
|
|
337
|
-
})
|
|
338
|
-
}
|
|
339
|
-
return destAttrs
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
mergeTranscriptAttributes(
|
|
343
|
-
firstTranscript: Feature | AnnotationFeature,
|
|
344
|
-
secondTranscript: AnnotationFeatureSnapshot,
|
|
345
|
-
) {
|
|
346
|
-
const txAttrs = this.mergeAttributes(firstTranscript, secondTranscript)
|
|
347
|
-
if (this.isAnnotationFeature(firstTranscript)) {
|
|
348
|
-
Object.entries(txAttrs).map(([key, value]) => {
|
|
349
|
-
firstTranscript.setAttribute(key, value)
|
|
350
|
-
})
|
|
351
|
-
} else {
|
|
352
|
-
firstTranscript.attributes = txAttrs
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
371
|
getInverse() {
|
|
357
372
|
const { assembly, changedIds, changes, logger } = this
|
|
358
373
|
const inverseChangedIds = [...changedIds].reverse()
|
package/src/Checks/CDSCheck.ts
CHANGED
|
@@ -22,7 +22,6 @@ enum CAUSES {
|
|
|
22
22
|
'InternalStopCodon',
|
|
23
23
|
'MissingStartCodon',
|
|
24
24
|
'MissingStopCodon',
|
|
25
|
-
'MultipleOfThree',
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
const iupacComplements: Record<string, string | undefined> = {
|
|
@@ -156,6 +155,10 @@ async function checkMRNA(
|
|
|
156
155
|
for (const cdsLocation of cdsLocations) {
|
|
157
156
|
const sequence = await getCDSSequence(cdsLocation, strand, getSequence)
|
|
158
157
|
const codons = splitSequenceInCodons(sequence)
|
|
158
|
+
const cdsEnd =
|
|
159
|
+
strand === -1
|
|
160
|
+
? cdsLocation.at(0)?.min ?? min
|
|
161
|
+
: cdsLocation.at(-1)?.max ?? max
|
|
159
162
|
if (sequence.length % 3 === 0) {
|
|
160
163
|
const start_codon = codons.at(0)
|
|
161
164
|
if (start_codon && !(start_codon.toUpperCase() in START_CODONS)) {
|
|
@@ -175,12 +178,9 @@ async function checkMRNA(
|
|
|
175
178
|
message: `Unexpected start codon in feature "${_id}": ${start_codon}`,
|
|
176
179
|
})
|
|
177
180
|
}
|
|
181
|
+
|
|
178
182
|
const lastCodon = codons.at(-1) // Last codon is supposed to be a stop
|
|
179
183
|
if (lastCodon && !(lastCodon.toUpperCase() in STOP_CODONS)) {
|
|
180
|
-
const cdsEnd =
|
|
181
|
-
strand === -1
|
|
182
|
-
? cdsLocation.at(0)?.min ?? min
|
|
183
|
-
: cdsLocation.at(-1)?.max ?? max
|
|
184
184
|
checkResults.push({
|
|
185
185
|
_id: new ObjectID().toHexString(),
|
|
186
186
|
name: CHECK_NAME,
|
|
@@ -196,12 +196,12 @@ async function checkMRNA(
|
|
|
196
196
|
checkResults.push({
|
|
197
197
|
_id: new ObjectID().toHexString(),
|
|
198
198
|
name: CHECK_NAME,
|
|
199
|
-
cause: CAUSES[CAUSES.
|
|
199
|
+
cause: CAUSES[CAUSES.MissingStopCodon],
|
|
200
200
|
ids,
|
|
201
201
|
refSeq: refSeq.toString(),
|
|
202
|
-
start:
|
|
203
|
-
end:
|
|
204
|
-
message: `The coding sequence for feature "${_id}" is not a multiple of three`,
|
|
202
|
+
start: cdsEnd,
|
|
203
|
+
end: cdsEnd,
|
|
204
|
+
message: `Missing stop codon: The coding sequence for feature "${_id}" is not a multiple of three`,
|
|
205
205
|
})
|
|
206
206
|
}
|
|
207
207
|
for (const [idx, codon] of codons.entries()) {
|
|
@@ -285,7 +285,7 @@ export class CDSCheck extends Check {
|
|
|
285
285
|
name = CHECK_NAME
|
|
286
286
|
causes = getCauses()
|
|
287
287
|
version = 1
|
|
288
|
-
|
|
288
|
+
isDefault = true
|
|
289
289
|
|
|
290
290
|
async checkFeature(
|
|
291
291
|
feature: AnnotationFeatureSnapshot,
|