Skip to content

Commit 6379ee4

Browse files
authoredMar 24, 2025··
Store taxonomy data per query + bug fix to reset taxon selection when switching to another query tab (#107)
* Fixed logic for collecting unclassified sequences * Update SankeyDiagram.vue * Edited condition to check for unclassified sequences * Reverted logic for counting unclassified node count * Store taxonomy data to each query * Store TaxonomyReports as list in SearchResult * Store taxonomy data per query + bug fix to reset taxon selection when switching to another query tab * Changed --report-mode parameter to 3
1 parent e91675c commit 6379ee4

File tree

5 files changed

+89
-40
lines changed

5 files changed

+89
-40
lines changed
 

‎backend/alignment.go

+51-20
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ type FastaEntry struct {
190190
type SearchResult struct {
191191
Database string `json:"db"`
192192
Alignments interface{} `json:"alignments"`
193-
TaxonomyReport interface{} `json:"taxonomyreport"`
193+
TaxonomyReports interface{} `json:"taxonomyreports"`
194194
}
195195

196196
type TaxonomyReport struct {
@@ -287,6 +287,8 @@ func ReadQueryByKeys(id Id, keys []uint32, jobsbase string) ([]FastaEntry, error
287287
func ReadAlignments[T any, U interface{ ~uint32 | ~int64 }](id Id, entries []U, databases []string, jobsbase string) ([]SearchResult, error) {
288288
base := filepath.Join(jobsbase, string(id))
289289
reader := Reader[uint32]{}
290+
taxonomyReader := Reader[uint32]{}
291+
290292
res := make([]SearchResult, 0)
291293

292294
var lookupByKey bool
@@ -305,8 +307,10 @@ func ReadAlignments[T any, U interface{ ~uint32 | ~int64 }](id Id, entries []U,
305307
if err != nil {
306308
return res, err
307309
}
308-
all := make([][]T, 0)
310+
311+
allAlignments := make([][]T, 0)
309312
for _, entry := range entries {
313+
// Get alignment body
310314
var body string
311315
if lookupByKey {
312316
alnKey := any(entry).(uint32)
@@ -318,7 +322,9 @@ func ReadAlignments[T any, U interface{ ~uint32 | ~int64 }](id Id, entries []U,
318322
body = reader.Data(alnId)
319323
} else {
320324
body = reader.Data(any(entry).(int64))
321-
}
325+
}
326+
327+
// Parse alignment
322328
data := strings.NewReader(body)
323329
results, err := ReadAlignment[T](data)
324330
if err != nil {
@@ -328,33 +334,58 @@ func ReadAlignments[T any, U interface{ ~uint32 | ~int64 }](id Id, entries []U,
328334
if len(results) == 0 {
329335
continue
330336
}
331-
all = append(all, results)
337+
338+
allAlignments = append(allAlignments, results)
332339
}
333340
reader.Delete()
334341

335-
// Read the taxonomy report
336-
taxonomyReportPath := filepath.Join(base, "alis_" + db + "_report")
337-
taxonomyReport, _ := ReadTaxonomyReport(taxonomyReportPath)
342+
allTaxonomyReports := make([][]TaxonomyReport, 0)
343+
if fileExists(name + "_report") {
344+
err = taxonomyReader.Make(dbpaths(name + "_report"))
345+
if err != nil {
346+
reader.Delete()
347+
return res, err
348+
}
349+
for _, entry := range entries {
350+
// Per-query taxonomy data
351+
var taxBody string
352+
if lookupByKey {
353+
taxKey := any(entry).(uint32)
354+
taxId, found := taxonomyReader.Id(taxKey)
355+
if !found {
356+
reader.Delete()
357+
taxonomyReader.Delete()
358+
return nil, fmt.Errorf("missing key: %T", taxKey)
359+
}
360+
taxBody = taxonomyReader.Data(taxId)
361+
} else {
362+
taxBody = taxonomyReader.Data(any(entry).(int64))
363+
}
364+
365+
// Parse single query’s taxonomy data
366+
taxResult, err := ReadTaxonomyReport(taxBody)
367+
if err != nil {
368+
taxonomyReader.Delete()
369+
return nil, err
370+
}
371+
372+
allTaxonomyReports = append(allTaxonomyReports, taxResult)
373+
}
374+
taxonomyReader.Delete()
375+
}
338376

339377
base := filepath.Base(name)
340378
res = append(res, SearchResult{
341379
strings.TrimPrefix(base, "alis_"),
342-
all,
343-
taxonomyReport, // Include taxonomy report
344-
})
345-
}
380+
allAlignments,
381+
allTaxonomyReports,
382+
})
383+
}
346384

347385
return res, nil
348386
}
349387

350-
func ReadTaxonomyReport(filePath string) ([]TaxonomyReport, error) {
351-
file, err := os.Open(filePath)
352-
if err != nil {
353-
// Return an empty report for any error
354-
return []TaxonomyReport{}, nil
355-
}
356-
defer file.Close()
357-
388+
func ReadTaxonomyReport(taxBody string) ([]TaxonomyReport, error) {
358389
// Helper function to count leading spaces
359390
countLeadingSpaces := func(s string) int {
360391
count := 0
@@ -369,7 +400,7 @@ func ReadTaxonomyReport(filePath string) ([]TaxonomyReport, error) {
369400
}
370401

371402
var reports []TaxonomyReport
372-
scanner := bufio.NewScanner(file)
403+
scanner := bufio.NewScanner(strings.NewReader(taxBody))
373404

374405
for scanner.Scan() {
375406
line := scanner.Text()

‎backend/worker.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ mv -f -- "${BASE}/query.lookup_tmp" "${BASE}/query.lookup"
394394

395395
if params.Taxonomy {
396396
parameters = append(parameters, "--report-mode")
397-
parameters = append(parameters, "0")
397+
parameters = append(parameters, "3")
398398
}
399399

400400
if params.Taxonomy && job.TaxFilter != "" {

‎frontend/Result.vue

+11
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
:hits="hits"
66
:selectedDatabases="selectedDatabases"
77
:tableMode="tableMode"
8+
:selectedTaxId="selectedTaxId"
89
/>
910
</template>
1011

@@ -46,6 +47,12 @@ export default {
4647
destroyed () {
4748
this.$root.$off('downloadJSON');
4849
},
50+
data() {
51+
return {
52+
selectedTaxId: null,
53+
54+
}
55+
},
4956
watch: {
5057
'$route': function(to, from) {
5158
if (from.path != to.path) {
@@ -63,6 +70,10 @@ export default {
6370
this.hits = null;
6471
this.selectedDatabases = 0;
6572
this.tableMode = 0;
73+
this.selectedTaxId = 0;
74+
this.$nextTick(() => {
75+
this.selectedTaxId = null;
76+
});
6677
},
6778
async fetchData() {
6879
this.resetProperties();

‎frontend/ResultView.vue

+12-5
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@
9898
</h2>
9999

100100
<!-- Button to toggle Sankey Diagram visibility -->
101-
<v-btn v-if="entry.hasTaxonomy" @click="toggleSankeyVisibility(entry.db)" class="mr-2" large>
101+
<v-btn v-if="entry.hasTaxonomy && !isComplex" @click="toggleSankeyVisibility(entry.db)" class="mr-2" large>
102102
{{ isSankeyVisible[entry.db] ? 'Hide Taxonomy' : 'Show Taxonomy' }}
103103
</v-btn>
104104

@@ -113,7 +113,7 @@
113113
</v-btn-toggle>
114114
</v-flex>
115115
<v-flex v-if="entry.hasTaxonomy && isSankeyVisible[entry.db]" class="mb-2">
116-
<SankeyDiagram :rawData="entry.taxonomyreport" :db="entry.db" :currentSelectedNodeId="selectedTaxId" :currentSelectedDb="selectedDb" @selectTaxon="handleSankeySelect"></SankeyDiagram>
116+
<SankeyDiagram :rawData="entry.taxonomyreports[0]" :db="entry.db" :currentSelectedNodeId="localSelectedTaxId" :currentSelectedDb="selectedDb" @selectTaxon="handleSankeySelect"></SankeyDiagram>
117117
</v-flex>
118118
<table class="v-table result-table" style="position:relativ; margin-bottom: 3em;">
119119
<colgroup>
@@ -283,7 +283,7 @@ export default {
283283
selectedDatabases: 0,
284284
isSankeyVisible: {}, // Track visibility for each db's Sankey Diagram
285285
selectedDb: null,
286-
selectedTaxId: null,
286+
localSelectedTaxId: null,
287287
filteredHitsTaxIds: [],
288288
tableMode: 0,
289289
menuActivator: null,
@@ -294,6 +294,7 @@ export default {
294294
ticket: "",
295295
error: "",
296296
hits: null,
297+
selectedTaxId: null,
297298
},
298299
created() {
299300
window.addEventListener("resize", this.handleAlignmentBoxResize, { passive: true });
@@ -344,6 +345,12 @@ export default {
344345
return "ERROR";
345346
}
346347
},
348+
watch: {
349+
selectedTaxId(newVal) {
350+
this.localSelectedTaxId = newVal;
351+
this.handleSankeySelect({ nodeId: newVal, db: this.selectedDb });
352+
}
353+
},
347354
methods: {
348355
log(args) {
349356
console.log(args);
@@ -383,13 +390,13 @@ export default {
383390
},
384391
handleSankeySelect({ nodeId, descendantIds, db }) {
385392
this.closeAlignment();
386-
this.selectedTaxId = nodeId;
393+
this.localSelectedTaxId = nodeId;
387394
this.filteredHitsTaxIds = descendantIds ? descendantIds.map(Number) : null;
388395
this.selectedDb = db;
389396
},
390397
handleChangeDatabase() {
391398
this.closeAlignment();
392-
this.selectedTaxId = null;
399+
this.localSelectedTaxId = null;
393400
this.filteredHitsTaxIds = [];
394401
},
395402
isGroupVisible(group) {

‎frontend/SankeyDiagram.vue

+14-14
Original file line numberDiff line numberDiff line change
@@ -343,18 +343,18 @@ export default {
343343
};
344344
345345
// // Define a clipping path for each link (crops out curve when links are too thick)
346-
// svg
347-
// .append("defs")
348-
// .selectAll("clipPath")
349-
// .data(graph.links)
350-
// .enter()
351-
// .append("clipPath")
352-
// // .attr("id", (d, i) => `clip-path-${this.instanceId}-${i}`)
353-
// .append("rect")
354-
// .attr("x", (d) => d.source.x1)
355-
// .attr("y", 0)
356-
// .attr("width", (d) => d.target.x0 - d.source.x1)
357-
// .attr("height", height);
346+
svg
347+
.append("defs")
348+
.selectAll("clipPath")
349+
.data(graph.links)
350+
.enter()
351+
.append("clipPath")
352+
.attr("id", (d, i) => `clip-path-${this.instanceId}-${i}`)
353+
.append("rect")
354+
.attr("x", (d) => d.source.x1)
355+
.attr("y", 0)
356+
.attr("width", (d) => d.target.x0 - d.source.x1)
357+
.attr("height", height);
358358
359359
// Add links
360360
svg
@@ -367,8 +367,8 @@ export default {
367367
.append("path")
368368
.attr("d", sankeyLinkHorizontal())
369369
.attr("stroke", (d) => (d.target.type === "unclassified" ? unclassifiedLabelColor : color(d.source.color)))
370-
.attr("stroke-width", (d) => Math.max(1, d.width));
371-
// .attr("clip-path", (d, i) => `url(#clip-path-${this.instanceId}-${i})`);
370+
.attr("stroke-width", (d) => Math.max(1, d.width))
371+
.attr("clip-path", (d, i) => `url(#clip-path-${this.instanceId}-${i})`);
372372
373373
// Create node group (node + labels) and add mouse events
374374
const nodeGroup = svg

0 commit comments

Comments
 (0)
Please sign in to comment.