Skip to content

Commit

Permalink
Redesign job stats page
Browse files Browse the repository at this point in the history
Move downloads to separate tab
Add bakta sequence stats
Improve breakpoints
  • Loading branch information
lukasjelonek committed Jan 7, 2025
1 parent 88a6ef0 commit e979b41
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 75 deletions.
4 changes: 2 additions & 2 deletions src/components/DisplayTuple.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ const props = withDefaults(
break: 4,
},
)
const labelClass = computed(() => 'col-md-' + props.break)
const valueClass = computed(() => 'col-md-' + (12 - props.break))
const labelClass = computed(() => 'col-' + props.break)
const valueClass = computed(() => 'col-' + (12 - props.break))
</script>

<style>
Expand Down
49 changes: 49 additions & 0 deletions src/components/bakta-result/BaktaDownloads.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<template>
<ul class="list-group">
<li class="list-group-item" v-for="d of downloads" :key="d.label">
<a :href="d.url" class="me-2"><i class="bi bi-download"></i></a>
<a :href="d.url"> {{ d.label }}</a>
</li>
</ul>
</template>
<script setup lang="ts">
import type { JobResult, ResultFileKey } from '@/model/job'
import { computed } from 'vue'
const props = defineProps<{
job: JobResult
}>()
const downloads = computed(() => {
type Count = { label: string; position: number }
const order: Record<ResultFileKey, Count> = {
TSV: { label: 'tsv', position: 0 },
GFF3: { label: 'gff3', position: 10 },
GBFF: { label: 'gbff', position: 20 },
EMBL: { label: 'embl', position: 25 },
FFN: { label: 'ffn', position: 30 },
FAA: { label: 'faa', position: 35 },
FNA: { label: 'fna', position: 40 },
JSON: { label: 'json', position: 50 },
TSVHypothetical: { label: 'tsv (hypothetical)', position: 55 },
TSVInterference: { label: 'tsv (interference)', position: 57 },
FAAHypothetical: { label: 'faa (hypothetical)', position: 60 },
PNGCircularPlot: { label: 'circular plot (png)', position: 70 },
SVGCircularPlot: { label: 'circular plot (svg)', position: 80 },
TXTLogs: { label: 'bakta logs', position: 90 },
}
const resultFiles: Record<string, string> =
props.job && props.job.ResultFiles ? props.job.ResultFiles : {}
const l: { key: string; label: string; position: number; url: string }[] = []
for (const k of Object.keys(resultFiles)) {
if (k in order) {
const _k = k as ResultFileKey
l.push({ key: k, url: resultFiles[_k], ...order[_k] })
} else {
l.push({ key: k, label: k, position: 1000, url: resultFiles[k] })
}
}
return l.sort((a, b) => a.position - b.position)
})
</script>
58 changes: 37 additions & 21 deletions src/components/bakta-result/BaktaResultVisualization.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<BaktaGenomeViewer v-if="currentTab === 'browser'" :data="bakta" />
<BaktaAnnotationTable v-if="currentTab === 'table'" :data="bakta" />
<FeaturePlotViewer v-if="currentTab === 'circular'" :bakta="bakta" />
<BaktaDownloads v-if="currentTab === 'download'" :job="job" />
</div>
<div
v-if="showShareButton"
Expand All @@ -38,35 +39,50 @@ import type { Result } from '@/model/result-data'
import BaktaGenomeViewer from './BaktaGenomeViewer.vue'
import BaktaAnnotationTable from './BaktaAnnotationTable.vue'
import BaktaStats from './BaktaStats.vue'
import { ref, useTemplateRef } from 'vue'
import { computed, ref, useTemplateRef } from 'vue'
import { Toast } from 'bootstrap'
import FeaturePlotViewer from './FeaturePlotViewer.vue'
import BaktaDownloads from './BaktaDownloads.vue'
defineProps<{
const props = defineProps<{
job: JobResult
bakta: Result
showShareButton: boolean
}>()
type tabs = 'job' | 'table' | 'browser' | 'circular'
type tabs = 'job' | 'table' | 'browser' | 'circular' | 'download'
const tabs: { key: tabs; label: string }[] = [
{
key: 'job',
label: 'Job statistics',
},
{
key: 'table',
label: 'Annotation table',
},
{
key: 'browser',
label: 'Genomeviewer',
},
{
key: 'circular',
label: 'Circular plot',
},
]
type TabDefinition = {
key: tabs
label: string
}
const tabs = computed<TabDefinition[]>(() => {
const tabs: TabDefinition[] = [
{
key: 'job',
label: 'Job statistics',
},
{
key: 'table',
label: 'Annotation table',
},
{
key: 'browser',
label: 'Genomeviewer',
},
{
key: 'circular',
label: 'Circular plot',
},
]
if (Object.keys(props.job.ResultFiles).length > 1)
tabs.push({
key: 'download',
label: 'Downloads',
})
return tabs
})
const currentTab = ref<tabs>('job')
const toast = useTemplateRef('copyToast')
function putLinkToClipboard() {
Expand Down
75 changes: 23 additions & 52 deletions src/components/bakta-result/BaktaStats.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<template>
<div class="row mb-5">
<div class="col-md-6">
<div class="col-md-6 col-sm-12">
<h5>Input</h5>
<display-tuple label="Organism:" :value="name" />
<display-tuple label="Sequences:" :value="sequencesCount" />
<display-tuple label="Genome size:" :value="size" />
</div>

<div v-if="job" class="col-md-6">
<div v-if="job" class="col-md-6 col-sm-12">
<h5>Runtime</h5>
<display-tuple label="Start:" :value="started" />
<display-tuple label="Stop:" :value="ended" />
Expand All @@ -16,50 +16,54 @@
</div>

<div class="row mb-5">
<div class="col-md-10">
<h5>Output</h5>
<div class="col-12">
<h5>Statistics</h5>
<div class="row mb-4">
<div class="col-md-4 col-sm-12">
<display-tuple :break="6" label="N50" :value="formatBp(data.stats.n50, 'bp')" />
<display-tuple :break="6" label="GC-content" :value="formatGc(data.stats.gc)" />
<display-tuple
:break="6"
label="Coding ratio"
:value="formatGc(data.stats.coding_ration)"
/>
<display-tuple :break="6" label="N-ratio" :value="formatGc(data.stats.n_ratio)" />
</div>
</div>
<h5>Feature counts (Total: {{ data.features.length }})</h5>
<div class="row">
<div class="col-md-4">
<div class="col-md-4 col-sm-12">
<display-tuple :break="6" label="tRNA:" :value="featureCount['tRNA']" />
<display-tuple :break="6" label="tmRNA:" :value="featureCount['tmRNA']" />
<display-tuple :break="6" label="rRNA:" :value="featureCount['rRNA']" />
<display-tuple :break="6" label="ncRNA:" :value="featureCount['ncRNA']" />
</div>
<div class="col-md-4">
<div class="col-md-4 col-sm-12">
<display-tuple :break="6" label="ncRNA regions:" :value="featureCount['ncRNA-region']" />
<display-tuple :break="6" label="CRISPR:" :value="featureCount['crispr']" />
<display-tuple :break="6" label="CDS:" :value="featureCount['cds']" />
<display-tuple :break="6" label="sORF:" :value="featureCount['sorf']" />
</div>
<div class="col-md-4">
<div class="col-md-4 col-sm-12">
<display-tuple :break="6" label="oriC:" :value="featureCount['oriC']" />
<display-tuple :break="6" label="oriV:" :value="featureCount['oriV']" />
<display-tuple :break="6" label="oriT:" :value="featureCount['oriT']" />
<display-tuple :break="6" label="gap:" :value="featureCount['gap']" />
</div>
<div class="col-md-3"></div>
</div>
</div>
</div>
<div v-if="job" class="row">
<h5>Downloads</h5>
<div class="col-12">
<template v-for="d in downloads" :key="d.key">
<a :href="d.url" class="text-no-wrap">
<span class="me-3">{{ d.label }}</span>
</a>
</template>
</div>
</div>
</template>

<script setup lang="ts">
import bakta from '@/bakta-helper'
import DisplayTuple from '@/components/DisplayTuple.vue'
import type { JobResult } from '@/model/job'
import type { Result } from '@/model/result-data'
import type { JobResult, ResultFileKey } from '@/model/job'
import humanizeDuration from 'humanize-duration'
import { computed } from 'vue'
import { formatBp } from './feature-plot/circluar-plot/formatters'
import { formatGc } from './feature-plot/formatters'
const props = withDefaults(
defineProps<{
Expand All @@ -77,39 +81,6 @@ const started = computed(() => formatDate(props.job.started))
const ended = computed(() => formatDate(props.job.updated))
const duration = computed(() => formatDuration(props.job.started, props.job.updated))
const downloads = computed(() => {
type Count = { label: string; position: number }
const order: Record<ResultFileKey, Count> = {
TSV: { label: 'tsv', position: 0 },
GFF3: { label: 'gff3', position: 10 },
GBFF: { label: 'gbff', position: 20 },
EMBL: { label: 'embl', position: 25 },
FFN: { label: 'ffn', position: 30 },
FAA: { label: 'faa', position: 35 },
FNA: { label: 'fna', position: 40 },
JSON: { label: 'json', position: 50 },
TSVHypothetical: { label: 'tsv (hypothetical)', position: 55 },
TSVInterference: { label: 'tsv (interference)', position: 57 },
FAAHypothetical: { label: 'faa (hypothetical)', position: 60 },
PNGCircularPlot: { label: 'circular plot (png)', position: 70 },
SVGCircularPlot: { label: 'circular plot (svg)', position: 80 },
TXTLogs: { label: 'bakta logs', position: 90 },
}
const resultFiles: Record<string, string> =
props.job && props.job.ResultFiles ? props.job.ResultFiles : {}
const l: { key: string; label: string; position: number; url: string }[] = []
for (const k of Object.keys(resultFiles)) {
if (k in order) {
const _k = k as ResultFileKey
l.push({ key: k, url: resultFiles[_k], ...order[_k] })
} else {
l.push({ key: k, label: k, position: 1000, url: resultFiles[k] })
}
}
return l.sort((a, b) => a.position - b.position)
})
function formatDate(date: string) {
return new Intl.DateTimeFormat('en-GB', {
dateStyle: 'short',
Expand Down
8 changes: 8 additions & 0 deletions src/model/result-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ export type Result = {
}
stats: {
size: number
gc: number
n_ratio: number
n50: number
coding_ration: number
}
features: Feature[]
sequences: Sequence[]
Expand Down Expand Up @@ -91,6 +95,10 @@ function toResult(input: BaktaResult | BaktaResult_1_9 | BaktaResult_1_10): Resu
},
stats: {
size: input.stats.size,
gc: input.stats.gc,
coding_ration: input.stats.coding_ratio,
n50: input.stats.n50,
n_ratio: input.stats.n_ratio,
},
features: input.features.map(toFeature),
sequences: input.sequences.map(toSequence),
Expand Down

0 comments on commit e979b41

Please sign in to comment.