msa-parsers 5.4.1 → 5.5.0
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/gff/gffToInterPro.d.ts +0 -10
- package/dist/gff/gffToInterPro.js +37 -5
- package/dist/gff/gffToInterPro.js.map +1 -1
- package/dist/gff/gffToInterPro.test.js +60 -0
- package/dist/gff/gffToInterPro.test.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/package.json +1 -1
- package/src/gff/gffToInterPro.test.ts +63 -0
- package/src/gff/gffToInterPro.ts +46 -7
- package/src/types.ts +5 -1
|
@@ -1,14 +1,4 @@
|
|
|
1
1
|
import type { GFFRecord, InterProScanResponse, InterProScanResults } from '../types.ts';
|
|
2
|
-
/**
|
|
3
|
-
* Convert GFF records to InterProScan format
|
|
4
|
-
*
|
|
5
|
-
* InterProScan GFF3 output format:
|
|
6
|
-
* - seq_id: sequence identifier
|
|
7
|
-
* - source: database/signature (e.g., "Pfam", "SMART")
|
|
8
|
-
* - type: usually "protein_match"
|
|
9
|
-
* - start/end: domain positions (1-based)
|
|
10
|
-
* - Attributes: Name (accession), signature_desc (name), Dbxref, etc.
|
|
11
|
-
*/
|
|
12
2
|
export declare function gffToInterProResults(gffRecords: GFFRecord[]): Record<string, InterProScanResults>;
|
|
13
3
|
/**
|
|
14
4
|
* Create a full InterProScanResponse from GFF records
|
|
@@ -8,6 +8,30 @@
|
|
|
8
8
|
* - start/end: domain positions (1-based)
|
|
9
9
|
* - Attributes: Name (accession), signature_desc (name), Dbxref, etc.
|
|
10
10
|
*/
|
|
11
|
+
// Feature types that read as a directional "gene" and get an arrowhead in the
|
|
12
|
+
// overlay. Exon/CDS/UTR/domain features deliberately stay rectangular blocks —
|
|
13
|
+
// turning every exon into an arrow is misleading, since exons are segments of a
|
|
14
|
+
// single transcript, not independently-oriented genes.
|
|
15
|
+
const GENE_LEVEL_TYPES = new Set([
|
|
16
|
+
'gene',
|
|
17
|
+
'pseudogene',
|
|
18
|
+
'mRNA',
|
|
19
|
+
'transcript',
|
|
20
|
+
'primary_transcript',
|
|
21
|
+
'ncRNA',
|
|
22
|
+
'tRNA',
|
|
23
|
+
'rRNA',
|
|
24
|
+
'snRNA',
|
|
25
|
+
'snoRNA',
|
|
26
|
+
'miRNA',
|
|
27
|
+
'lnc_RNA',
|
|
28
|
+
]);
|
|
29
|
+
// +1/-1 for a stranded gene-level feature, undefined otherwise (which the
|
|
30
|
+
// renderer draws as a plain block).
|
|
31
|
+
function geneStrand({ type, strand }) {
|
|
32
|
+
const directional = strand === '+' ? 1 : strand === '-' ? -1 : undefined;
|
|
33
|
+
return GENE_LEVEL_TYPES.has(type) ? directional : undefined;
|
|
34
|
+
}
|
|
11
35
|
export function gffToInterProResults(gffRecords) {
|
|
12
36
|
const bySequence = new Map();
|
|
13
37
|
for (const record of gffRecords) {
|
|
@@ -35,16 +59,24 @@ export function gffToInterProResults(gffRecords) {
|
|
|
35
59
|
record.Note ||
|
|
36
60
|
name;
|
|
37
61
|
if (!matchInfo.has(accession)) {
|
|
38
|
-
matchInfo.set(accession, {
|
|
62
|
+
matchInfo.set(accession, {
|
|
63
|
+
name,
|
|
64
|
+
description,
|
|
65
|
+
accession,
|
|
66
|
+
featureType: record.type,
|
|
67
|
+
});
|
|
39
68
|
}
|
|
69
|
+
const location = {
|
|
70
|
+
start: record.start,
|
|
71
|
+
end: record.end,
|
|
72
|
+
strand: geneStrand(record),
|
|
73
|
+
};
|
|
40
74
|
const locations = matchesByAccession.get(accession);
|
|
41
75
|
if (locations) {
|
|
42
|
-
locations.push(
|
|
76
|
+
locations.push(location);
|
|
43
77
|
}
|
|
44
78
|
else {
|
|
45
|
-
matchesByAccession.set(accession, [
|
|
46
|
-
{ start: record.start, end: record.end },
|
|
47
|
-
]);
|
|
79
|
+
matchesByAccession.set(accession, [location]);
|
|
48
80
|
}
|
|
49
81
|
}
|
|
50
82
|
const matches = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gffToInterPro.js","sourceRoot":"","sources":["../../src/gff/gffToInterPro.ts"],"names":[],"mappings":"AAMA;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CAClC,UAAuB;IAEvB,MAAM,UAAU,GAAG,IAAI,GAAG,EAAuB,CAAA;IAEjD,KAAK,MAAM,MAAM,IAAI,UAAU,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAC9C,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACvB,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;QACzC,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAwC,EAAE,CAAA;IAEvD,KAAK,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,UAAU,EAAE,CAAC;QAC1C,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAG/B,CAAA;QACH,MAAM,SAAS,GAAG,IAAI,GAAG,
|
|
1
|
+
{"version":3,"file":"gffToInterPro.js","sourceRoot":"","sources":["../../src/gff/gffToInterPro.ts"],"names":[],"mappings":"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"}
|
|
@@ -27,6 +27,7 @@ describe('gffToInterProResults', () => {
|
|
|
27
27
|
accession: 'PF00001',
|
|
28
28
|
name: '7tm_1',
|
|
29
29
|
description: 'GPCR family',
|
|
30
|
+
featureType: 'protein_match',
|
|
30
31
|
});
|
|
31
32
|
expect(result.seq1?.matches[0]?.locations).toEqual([{ start: 10, end: 50 }]);
|
|
32
33
|
expect(result.seq1?.xref).toEqual([{ id: 'seq1' }]);
|
|
@@ -123,6 +124,65 @@ describe('gffToInterProResults', () => {
|
|
|
123
124
|
{ start: 100, end: 150 },
|
|
124
125
|
]);
|
|
125
126
|
});
|
|
127
|
+
test('gives gene-level features a direction but leaves exons/domains as blocks', () => {
|
|
128
|
+
const records = [
|
|
129
|
+
{
|
|
130
|
+
seq_id: 'seq1',
|
|
131
|
+
source: 'x',
|
|
132
|
+
type: 'gene',
|
|
133
|
+
start: 5,
|
|
134
|
+
end: 50,
|
|
135
|
+
score: 0,
|
|
136
|
+
strand: '+',
|
|
137
|
+
phase: '.',
|
|
138
|
+
Name: 'GENEA',
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
seq_id: 'seq1',
|
|
142
|
+
source: 'x',
|
|
143
|
+
type: 'mRNA',
|
|
144
|
+
start: 60,
|
|
145
|
+
end: 90,
|
|
146
|
+
score: 0,
|
|
147
|
+
strand: '-',
|
|
148
|
+
phase: '.',
|
|
149
|
+
Name: 'GENEB',
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
seq_id: 'seq1',
|
|
153
|
+
source: 'x',
|
|
154
|
+
type: 'exon',
|
|
155
|
+
start: 5,
|
|
156
|
+
end: 20,
|
|
157
|
+
score: 0,
|
|
158
|
+
strand: '+',
|
|
159
|
+
phase: '.',
|
|
160
|
+
Name: 'exon-1',
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
seq_id: 'seq1',
|
|
164
|
+
source: 'x',
|
|
165
|
+
type: 'protein_match',
|
|
166
|
+
start: 5,
|
|
167
|
+
end: 20,
|
|
168
|
+
score: 0,
|
|
169
|
+
strand: '+',
|
|
170
|
+
phase: '.',
|
|
171
|
+
Name: 'PF00001',
|
|
172
|
+
},
|
|
173
|
+
];
|
|
174
|
+
const result = gffToInterProResults(records);
|
|
175
|
+
const byAccession = Object.fromEntries(result.seq1.matches.map(m => [
|
|
176
|
+
m.signature.entry.accession,
|
|
177
|
+
m.locations[0].strand,
|
|
178
|
+
]));
|
|
179
|
+
expect(byAccession).toEqual({
|
|
180
|
+
GENEA: 1, // + gene -> arrow right
|
|
181
|
+
GENEB: -1, // - gene -> arrow left
|
|
182
|
+
'exon-1': undefined, // exon stays a block even though it is stranded
|
|
183
|
+
PF00001: undefined, // protein domain stays a block
|
|
184
|
+
});
|
|
185
|
+
});
|
|
126
186
|
test('uses ID as fallback for Name', () => {
|
|
127
187
|
const records = [
|
|
128
188
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gffToInterPro.test.js","sourceRoot":"","sources":["../../src/gff/gffToInterPro.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAE/C,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAIhF,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,IAAI,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAChC,MAAM,CAAC,oBAAoB,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IAC9C,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACtC,MAAM,OAAO,GAAgB;YAC3B;gBACE,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,MAAM;gBACd,IAAI,EAAE,eAAe;gBACrB,KAAK,EAAE,EAAE;gBACT,GAAG,EAAE,EAAE;gBACP,KAAK,EAAE,CAAC;gBACR,MAAM,EAAE,GAAG;gBACX,KAAK,EAAE,GAAG;gBACV,IAAI,EAAE,SAAS;gBACf,cAAc,EAAE,OAAO;gBACvB,WAAW,EAAE,aAAa;aAC3B;SACF,CAAA;QACD,MAAM,MAAM,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAA;QAE5C,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,CAAA;QACrC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;QAC5C,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC;YACvD,SAAS,EAAE,SAAS;YACpB,IAAI,EAAE,OAAO;YACb,WAAW,EAAE,aAAa;
|
|
1
|
+
{"version":3,"file":"gffToInterPro.test.js","sourceRoot":"","sources":["../../src/gff/gffToInterPro.test.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -34,11 +34,13 @@ export interface InterProScanMatch {
|
|
|
34
34
|
name: string;
|
|
35
35
|
description: string;
|
|
36
36
|
accession: string;
|
|
37
|
+
featureType?: string;
|
|
37
38
|
};
|
|
38
39
|
};
|
|
39
40
|
locations: {
|
|
40
41
|
start: number;
|
|
41
42
|
end: number;
|
|
43
|
+
strand?: number;
|
|
42
44
|
}[];
|
|
43
45
|
}
|
|
44
46
|
export interface InterProScanResults {
|
package/package.json
CHANGED
|
@@ -33,6 +33,7 @@ describe('gffToInterProResults', () => {
|
|
|
33
33
|
accession: 'PF00001',
|
|
34
34
|
name: '7tm_1',
|
|
35
35
|
description: 'GPCR family',
|
|
36
|
+
featureType: 'protein_match',
|
|
36
37
|
})
|
|
37
38
|
expect(result.seq1?.matches[0]?.locations).toEqual([{ start: 10, end: 50 }])
|
|
38
39
|
expect(result.seq1?.xref).toEqual([{ id: 'seq1' }])
|
|
@@ -136,6 +137,68 @@ describe('gffToInterProResults', () => {
|
|
|
136
137
|
])
|
|
137
138
|
})
|
|
138
139
|
|
|
140
|
+
test('gives gene-level features a direction but leaves exons/domains as blocks', () => {
|
|
141
|
+
const records: GFFRecord[] = [
|
|
142
|
+
{
|
|
143
|
+
seq_id: 'seq1',
|
|
144
|
+
source: 'x',
|
|
145
|
+
type: 'gene',
|
|
146
|
+
start: 5,
|
|
147
|
+
end: 50,
|
|
148
|
+
score: 0,
|
|
149
|
+
strand: '+',
|
|
150
|
+
phase: '.',
|
|
151
|
+
Name: 'GENEA',
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
seq_id: 'seq1',
|
|
155
|
+
source: 'x',
|
|
156
|
+
type: 'mRNA',
|
|
157
|
+
start: 60,
|
|
158
|
+
end: 90,
|
|
159
|
+
score: 0,
|
|
160
|
+
strand: '-',
|
|
161
|
+
phase: '.',
|
|
162
|
+
Name: 'GENEB',
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
seq_id: 'seq1',
|
|
166
|
+
source: 'x',
|
|
167
|
+
type: 'exon',
|
|
168
|
+
start: 5,
|
|
169
|
+
end: 20,
|
|
170
|
+
score: 0,
|
|
171
|
+
strand: '+',
|
|
172
|
+
phase: '.',
|
|
173
|
+
Name: 'exon-1',
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
seq_id: 'seq1',
|
|
177
|
+
source: 'x',
|
|
178
|
+
type: 'protein_match',
|
|
179
|
+
start: 5,
|
|
180
|
+
end: 20,
|
|
181
|
+
score: 0,
|
|
182
|
+
strand: '+',
|
|
183
|
+
phase: '.',
|
|
184
|
+
Name: 'PF00001',
|
|
185
|
+
},
|
|
186
|
+
]
|
|
187
|
+
const result = gffToInterProResults(records)
|
|
188
|
+
const byAccession = Object.fromEntries(
|
|
189
|
+
result.seq1!.matches.map(m => [
|
|
190
|
+
m.signature.entry!.accession,
|
|
191
|
+
m.locations[0]!.strand,
|
|
192
|
+
]),
|
|
193
|
+
)
|
|
194
|
+
expect(byAccession).toEqual({
|
|
195
|
+
GENEA: 1, // + gene -> arrow right
|
|
196
|
+
GENEB: -1, // - gene -> arrow left
|
|
197
|
+
'exon-1': undefined, // exon stays a block even though it is stranded
|
|
198
|
+
PF00001: undefined, // protein domain stays a block
|
|
199
|
+
})
|
|
200
|
+
})
|
|
201
|
+
|
|
139
202
|
test('uses ID as fallback for Name', () => {
|
|
140
203
|
const records: GFFRecord[] = [
|
|
141
204
|
{
|
package/src/gff/gffToInterPro.ts
CHANGED
|
@@ -14,6 +14,32 @@ import type {
|
|
|
14
14
|
* - start/end: domain positions (1-based)
|
|
15
15
|
* - Attributes: Name (accession), signature_desc (name), Dbxref, etc.
|
|
16
16
|
*/
|
|
17
|
+
// Feature types that read as a directional "gene" and get an arrowhead in the
|
|
18
|
+
// overlay. Exon/CDS/UTR/domain features deliberately stay rectangular blocks —
|
|
19
|
+
// turning every exon into an arrow is misleading, since exons are segments of a
|
|
20
|
+
// single transcript, not independently-oriented genes.
|
|
21
|
+
const GENE_LEVEL_TYPES = new Set([
|
|
22
|
+
'gene',
|
|
23
|
+
'pseudogene',
|
|
24
|
+
'mRNA',
|
|
25
|
+
'transcript',
|
|
26
|
+
'primary_transcript',
|
|
27
|
+
'ncRNA',
|
|
28
|
+
'tRNA',
|
|
29
|
+
'rRNA',
|
|
30
|
+
'snRNA',
|
|
31
|
+
'snoRNA',
|
|
32
|
+
'miRNA',
|
|
33
|
+
'lnc_RNA',
|
|
34
|
+
])
|
|
35
|
+
|
|
36
|
+
// +1/-1 for a stranded gene-level feature, undefined otherwise (which the
|
|
37
|
+
// renderer draws as a plain block).
|
|
38
|
+
function geneStrand({ type, strand }: GFFRecord): number | undefined {
|
|
39
|
+
const directional = strand === '+' ? 1 : strand === '-' ? -1 : undefined
|
|
40
|
+
return GENE_LEVEL_TYPES.has(type) ? directional : undefined
|
|
41
|
+
}
|
|
42
|
+
|
|
17
43
|
export function gffToInterProResults(
|
|
18
44
|
gffRecords: GFFRecord[],
|
|
19
45
|
): Record<string, InterProScanResults> {
|
|
@@ -33,11 +59,16 @@ export function gffToInterProResults(
|
|
|
33
59
|
for (const [seqId, records] of bySequence) {
|
|
34
60
|
const matchesByAccession = new Map<
|
|
35
61
|
string,
|
|
36
|
-
{ start: number; end: number }[]
|
|
62
|
+
{ start: number; end: number; strand?: number }[]
|
|
37
63
|
>()
|
|
38
64
|
const matchInfo = new Map<
|
|
39
65
|
string,
|
|
40
|
-
{
|
|
66
|
+
{
|
|
67
|
+
name: string
|
|
68
|
+
description: string
|
|
69
|
+
accession: string
|
|
70
|
+
featureType: string
|
|
71
|
+
}
|
|
41
72
|
>()
|
|
42
73
|
|
|
43
74
|
for (const record of records) {
|
|
@@ -56,16 +87,24 @@ export function gffToInterProResults(
|
|
|
56
87
|
name
|
|
57
88
|
|
|
58
89
|
if (!matchInfo.has(accession)) {
|
|
59
|
-
matchInfo.set(accession, {
|
|
90
|
+
matchInfo.set(accession, {
|
|
91
|
+
name,
|
|
92
|
+
description,
|
|
93
|
+
accession,
|
|
94
|
+
featureType: record.type,
|
|
95
|
+
})
|
|
60
96
|
}
|
|
61
97
|
|
|
98
|
+
const location = {
|
|
99
|
+
start: record.start,
|
|
100
|
+
end: record.end,
|
|
101
|
+
strand: geneStrand(record),
|
|
102
|
+
}
|
|
62
103
|
const locations = matchesByAccession.get(accession)
|
|
63
104
|
if (locations) {
|
|
64
|
-
locations.push(
|
|
105
|
+
locations.push(location)
|
|
65
106
|
} else {
|
|
66
|
-
matchesByAccession.set(accession, [
|
|
67
|
-
{ start: record.start, end: record.end },
|
|
68
|
-
])
|
|
107
|
+
matchesByAccession.set(accession, [location])
|
|
69
108
|
}
|
|
70
109
|
}
|
|
71
110
|
|
package/src/types.ts
CHANGED
|
@@ -38,9 +38,13 @@ export interface InterProScanMatch {
|
|
|
38
38
|
name: string
|
|
39
39
|
description: string
|
|
40
40
|
accession: string
|
|
41
|
+
// original GFF feature type (exon, CDS, gene, ...) when sourced from GFF;
|
|
42
|
+
// lets the viewer treat ordinal segments (exons) differently from
|
|
43
|
+
// categorical domains
|
|
44
|
+
featureType?: string
|
|
41
45
|
}
|
|
42
46
|
}
|
|
43
|
-
locations: { start: number; end: number }[]
|
|
47
|
+
locations: { start: number; end: number; strand?: number }[]
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
export interface InterProScanResults {
|