Skip to content

Commit

Permalink
Merge pull request #96 from snjlee58/master
Browse files Browse the repository at this point in the history
Added sankey plot using taxonomy report
  • Loading branch information
milot-mirdita authored Jan 2, 2025
2 parents 3e1d5a7 + a56738d commit 7654f55
Show file tree
Hide file tree
Showing 6 changed files with 1,669 additions and 16 deletions.
70 changes: 69 additions & 1 deletion backend/alignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,16 @@ type FastaEntry struct {
type SearchResult struct {
Database string `json:"db"`
Alignments interface{} `json:"alignments"`
TaxonomyReport interface{} `json:"taxonomyreport"`
}

type TaxonomyReport struct {
Proportion float64 `json:"proportion"`
CladeReads int `json:"clade_reads"`
TaxonReads int `json:"taxon_reads"`
Rank string `json:"rank"`
TaxonID string `json:"taxon_id"`
ScientificName string `json:"name"`
}

func dbpaths(path string) (string, string) {
Expand Down Expand Up @@ -320,13 +330,71 @@ func ReadAlignments[T any, U interface{ ~uint32 | ~int64 }](id Id, entries []U,
all = append(all, results)
}
reader.Delete()

// Read the taxonomy report
taxonomyReportPath := filepath.Join(base, "alis_" + db + "_report")
taxonomyReport, _ := ReadTaxonomyReport(taxonomyReportPath)

base := filepath.Base(name)
res = append(res, SearchResult{strings.TrimPrefix(base, "alis_"), all})
res = append(res, SearchResult{
strings.TrimPrefix(base, "alis_"),
all,
taxonomyReport, // Include taxonomy report
})
}

return res, nil
}

func ReadTaxonomyReport(filePath string) ([]TaxonomyReport, error) {
file, err := os.Open(filePath)
if err != nil {
// Return an empty report for any error
return []TaxonomyReport{}, nil
}
defer file.Close()

var reports []TaxonomyReport
scanner := bufio.NewScanner(file)

for scanner.Scan() {
line := scanner.Text()
fields := strings.Split(line, "\t")
if len(fields) < 6 {
// Skip invalid lines
continue
}

// Parse the fields
proportion, err := strconv.ParseFloat(fields[0], 64)
if err != nil {
continue
}

cladeReads, err := strconv.Atoi(fields[1])
if err != nil {
continue
}

taxonReads, err := strconv.Atoi(fields[2])
if err != nil {
continue
}

report := TaxonomyReport{
Proportion: proportion,
CladeReads: cladeReads,
TaxonReads: taxonReads,
Rank: fields[3],
TaxonID: fields[4],
ScientificName: strings.TrimSpace(fields[5]),
}
reports = append(reports, report)
}

return reports, nil
}

func Alignments(id Id, entry []int64, databases []string, jobsbase string) ([]SearchResult, error) {
return ReadAlignments[AlignmentEntry, int64](id, entry, databases, jobsbase)
}
Expand Down
48 changes: 41 additions & 7 deletions frontend/ResultView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,17 @@
<v-tab v-for="entry in hits.results" :key="entry.db">{{ entry.db }} ({{ entry.alignments ? Object.values(entry.alignments).length : 0 }})</v-tab>
</v-tabs>
<div v-for="(entry, index) in hits.results" :key="entry.db" v-if="selectedDatabases == 0 || (index + 1) == selectedDatabases">
<v-flex class="d-flex" :style="{ 'flex-direction' : $vuetify.breakpoint.xsOnly ? 'column' : null }">
<v-flex class="d-flex" :style="{ 'flex-direction' : $vuetify.breakpoint.xsOnly ? 'column' : null, 'align-items': 'center' }">
<h2 style="margin-top: 0.5em; margin-bottom: 1em; display: inline-block;">
<span style="text-transform: uppercase;">{{ entry.db }}</span> <small>{{ entry.alignments ? Object.values(entry.alignments).length : 0 }} hits</small>
</h2>
<v-btn-toggle mandatory v-model="tableMode" class="ml-auto">

<!-- Button to toggle Sankey Diagram visibility -->
<v-btn @click="isSankeyVisible = !isSankeyVisible" class="ml-auto mr-2" large>
{{ isSankeyVisible ? 'Hide Sankey' : 'Show Sankey' }}
</v-btn>

<v-btn-toggle mandatory v-model="tableMode" >
<v-btn>
Graphical
</v-btn>
Expand All @@ -106,7 +112,9 @@
</v-btn>
</v-btn-toggle>
</v-flex>

<v-flex v-if="isSankeyVisible && entry.taxonomyreport">
<SankeyDiagram :rawData="entry.taxonomyreport" :currentSelectedNodeId="selectedTaxId" @selectTaxon="handleSankeySelect"></SankeyDiagram>
</v-flex>
<table class="v-table result-table" style="position:relativ; margin-bottom: 3em;">
<colgroup>
<template v-if="isComplex">
Expand Down Expand Up @@ -177,7 +185,7 @@
</tr>
</thead>
<tbody>
<template v-for="(group, groupidx) in entry.alignments">
<template v-for="(group, groupidx) in filteredAlignments(entry.alignments)" >
<tr v-for="(item, index) in group" :class="['hit', { 'active' : item.active }]">
<template v-if="index == 0 && isComplex">
<td class="thin" data-label="Query TM-score" :rowspan="group.length">{{ group[0].complexqtm.toFixed(2) }}</td>
Expand Down Expand Up @@ -251,6 +259,7 @@
import Panel from './Panel.vue';
import AlignmentPanel from './AlignmentPanel.vue';
import Ruler from './Ruler.vue';
import SankeyDiagram from './SankeyDiagram.vue';
import { debounce } from './lib/debounce';
Expand All @@ -265,13 +274,16 @@ function getAbsOffsetTop($el) {
export default {
name: 'ResultView',
components: { Panel, AlignmentPanel, Ruler },
components: { Panel, AlignmentPanel, Ruler, SankeyDiagram },
data() {
return {
alignment: null,
activeTarget: null,
alnBoxOffset: 0,
selectedDatabases: 0,
isSankeyVisible: false,
selectedTaxId: null,
filteredHitsTaxIds: [],
tableMode: 0,
menuActivator: null,
menuItems: [],
Expand Down Expand Up @@ -363,7 +375,30 @@ export default {
this.menuItems = items;
this.menuActivator.click(event);
}
}
},
handleSankeySelect({ nodeId, descendantIds }) {
this.selectedTaxId = nodeId;
this.filteredHitsTaxIds = descendantIds ? descendantIds.map(Number) : null;
},
filteredAlignments(alignments) {
// Convert alignments to an array if it is an object
if (alignments && !Array.isArray(alignments)) {
alignments = Object.values(alignments);
}
if (!Array.isArray(alignments)) {
return []; // Return an empty array if conversion fails
}
if (!this.filteredHitsTaxIds || this.filteredHitsTaxIds.length === 0) {
return alignments; // Return all groups if no filteredAlignments
}
// Filter each group to only include items with taxId in filteredAlignments
return alignments
.map(group => group.filter(item => this.filteredHitsTaxIds.includes(Number(item.taxId))))
.filter(group => group.length > 0);
},
}
};
</script>
Expand Down Expand Up @@ -424,7 +459,6 @@ src: url(assets/InconsolataClustal2.woff2),
}
}
.theme--dark {
.result-table {
a:not([href]) {
Expand Down
Loading

0 comments on commit 7654f55

Please sign in to comment.