Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: group BUSCO stats into collapsible sections for better display #148

Merged
merged 34 commits into from
May 1, 2024
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b630095
ENH: group BUSCO stats into collapsible sections for better display
misialq Mar 13, 2024
ee9be31
ENH: introduce BUSCOResults types and formats
misialq Mar 19, 2024
6d069cb
refactor into separate evaluation/visualization actions
misialq Mar 19, 2024
0fd03a9
Add some debug prints
misialq Mar 19, 2024
2d3069a
BUG: adjust MANIFEST while partitioning MAGs
misialq Mar 19, 2024
8e87f2a
Add some debug prints while partitioning MAGs
misialq Mar 19, 2024
edf462a
fixup! BUG: adjust MANIFEST while partitioning MAGs
misialq Mar 19, 2024
9d7ee2d
FIX: SampleData[MAGs] partitioning should not split MAGs from the sam…
misialq Mar 19, 2024
be8de54
ENH: split evaluate_busco into two subactions and convert into a pipe…
misialq Mar 19, 2024
31d78ce
fixup! FIX: SampleData[MAGs] partitioning should not split MAGs from …
misialq Mar 19, 2024
aac754a
Complete overhaul of BUSCO visualization
misialq Mar 23, 2024
ab9e1b4
Little updates to the feature table
misialq Mar 25, 2024
36b57f9
Add length calculation
misialq Mar 25, 2024
fceef4a
Add length to the BUSCO viz table
misialq Mar 28, 2024
bbca1bc
Add first tests
misialq Apr 18, 2024
a7303d4
Merge remote-tracking branch 'upstream/main' into fix-busco
misialq Apr 18, 2024
f87f985
Lint
misialq Apr 18, 2024
19f6f0c
Add missing files
misialq Apr 18, 2024
89a3870
More missing files
misialq Apr 18, 2024
c4563fe
Fix partition tests
misialq Apr 19, 2024
530f9d0
Add more tests
misialq Apr 19, 2024
a88bc02
Remove unnecessary test files
misialq Apr 19, 2024
b414606
Update the Codecov GA to v4
misialq Apr 19, 2024
fdb6443
Add tests for fromats and types
misialq Apr 19, 2024
db4d40f
Lint
misialq Apr 19, 2024
f4811ca
Merge branch 'main' into fix-busco
misialq Apr 22, 2024
e04fe13
Add more tests
misialq Apr 23, 2024
a4d2f32
Add final test
misialq Apr 23, 2024
08b002a
Lint
misialq Apr 23, 2024
65aa85f
Apply suggestions from code review
misialq Apr 24, 2024
ea49b8a
Review requests and updates
misialq May 1, 2024
fe3de47
Lint
misialq May 1, 2024
b3b9015
Merge branch 'main' into fix-busco
misialq May 1, 2024
37a7241
Lint again
misialq May 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions q2_moshpit/assets/busco/css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,91 @@
margin-top: 8px;
margin-bottom: 8px;
}

/*adjustment for Bootstrap 5*/
.table {
border: 1px solid #dee2e6;
margin-top: 10px
}

.pagination {
justify-content: right;
}

/* SPINKIT */

/*
The MIT License (MIT)

Copyright (c) 2015-2020 Tobias Ahlin

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

.spinner {
margin: 100px auto;
width: 50px;
height: 40px;
text-align: center;
font-size: 10px;
}

.spinner > div {
background-color: #333;
height: 100%;
width: 6px;
display: inline-block;

-webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out;
animation: sk-stretchdelay 1.2s infinite ease-in-out;
}

.spinner .rect2 {
-webkit-animation-delay: -1.1s;
animation-delay: -1.1s;
}

.spinner .rect3 {
-webkit-animation-delay: -1.0s;
animation-delay: -1.0s;
}

.spinner .rect4 {
-webkit-animation-delay: -0.9s;
animation-delay: -0.9s;
}

.spinner .rect5 {
-webkit-animation-delay: -0.8s;
animation-delay: -0.8s;
}

@-webkit-keyframes sk-stretchdelay {
0%, 40%, 100% { -webkit-transform: scaleY(0.4) }
20% { -webkit-transform: scaleY(1.0) }
}

@keyframes sk-stretchdelay {
0%, 40%, 100% {
transform: scaleY(0.4);
-webkit-transform: scaleY(0.4);
} 20% {
transform: scaleY(1.0);
-webkit-transform: scaleY(1.0);
}
}
258 changes: 258 additions & 0 deletions q2_moshpit/assets/busco/detailed_view.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
{% extends "tabbed.html" %}

{% block head %}
<title>Embedding Vega-Lite</title>
<script src="js/bootstrapMagic.js" type="text/javascript"></script>
<link href="css/styles.css" rel="stylesheet" />
<script type="text/javascript">
// temporary hack to make it look good with Bootstrap 5
removeBS3refs();
</script>
<script src="https://cdn.jsdelivr.net/npm//vega@5" type="text/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm//[email protected]" type="text/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm//vega-embed@6" type="text/javascript"></script>
<link crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha256-YvdLHPgkqJ8DVUxjjnGVlMMJtNimJ6dYkowFFvp4kKs=" rel="stylesheet"/>
{% endblock %}

{% block tabcontent %}
<script crossorigin="anonymous" integrity="sha256-9SEPo+fwJFpMUet/KACSwO+Z/dKMReF9q4zFhU/fT9M=" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>

<br>
<div class="row row-cols-1 row-cols-md-2 g-4">
<div class="col-lg-12">
<div class="card mt-3 h-10">
<h5 class="card-header">Plot description</h5>
<div class="card-body">
<p>
The left plot shows the results generated by BUSCO for <b>all bins</b> and
misialq marked this conversation as resolved.
Show resolved Hide resolved
<b> samples</b>. "BUSCO attempts to provide a quantitative assessment
of the completeness in terms of the expected gene content of a genome
assembly, transcriptome, or annotated gene set. The results are
simplified into categories of complete and single-copy, complete and
duplicated, fragmented, or missing BUSCOs. BUSCO completeness results
make sense only in the context of the biology of your organism". Visit the
<a href="https://busco.ezlab.org/busco_userguide.html#interpreting-the-results">BUSCO User Guide </a>
for more information.
</p>
<p>
The right barplot shows assembly statistics calculated for each bin using BBTools.
misialq marked this conversation as resolved.
Show resolved Hide resolved
Specifically, it displays the statistics computed by the <b>stats.sh</b> procedure from BBMap.
View the
<a href="https://github.com/BioInfoTools/BBMap/blob/master/sh/stats.sh">
source code and documentation
</a>
of stats.sh for more information.
</p>

<div style="align-items: center; display: flex">
<span class="header-inline">Downloads:</span>
<div class="'col-lg-4">
<div aria-label="Basic outlined example" class="btn-group" role="group">
Sann5 marked this conversation as resolved.
Show resolved Hide resolved
<a class="btn btn-outline-secondary" href="busco_results.csv">BUSCO results (csv)</a>
</div>
</div>
</div>
</div>
</div>

<div class="card mt-3 h-10">
<h5 class="card-header">Plot controls</h5>
<div class="card-body">
<p>
To prevent data overload, the samples are grouped into collapsible sections below. You can
expand/collapse each section by clicking on the little arrow on the right side of the section header.
Each section will contain a maximum of 100 bins.
<br><br>
If you want to find statistics for a specific sample, you can pick your sample from the dropdown below -
this will find the appropriate section and expand it.
<br><br>
You can hover over the bars to obtain information about the lineage dataset used for each bin,
misialq marked this conversation as resolved.
Show resolved Hide resolved
and the number of genes in each BUSCO category. Additionally, you can choose the assembly statistic
that you wish to display from the dropdown menu included in every section.
misialq marked this conversation as resolved.
Show resolved Hide resolved
</p>
<div id="plot-controls">
<div style="align-items: center; display: flex;">
<span class="header-inline">Find sample:</span>
<div class="col-lg-4">
<div class="btn-group" role="group">
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" id="sampleSelector" data-bs-toggle="dropdown" aria-expanded="false">
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton1" id="sampleSelectorElements"></ul>
</div>
<button id="collapseAllButton" class="btn btn-outline-secondary" type="submit" style="white-space: nowrap;">Collapse all</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

<div class="row">
<div class="col-lg-12">
<div id="plot">
<div class="accordion" id="accordionSamples"></div>
</div>
</div>
</div>

<script id="vega_json" type="application/json">
{{ vega_json | safe }}
</script>

<script type="text/javascript">
function createAccordionItem(sampleFrom, sampleTo, show) {
// Create elements
const accordionItem = document.createElement('div');
const accordionHeader = document.createElement('h2');
const accordionButton = document.createElement('button');
const accordionCollapse = document.createElement('div');
const accordionBody = document.createElement('div');

// Set attributes and content
accordionItem.className = 'accordion-item';
accordionHeader.className = 'accordion-header';
// accordionHeader.id = 'headingOne';
accordionButton.className = 'accordion-button';
accordionButton.type = 'button';
accordionButton.setAttribute('data-bs-toggle', 'collapse');
accordionButton.setAttribute('data-bs-target', `#collapse-${sampleFrom}`);
// accordionButton.setAttribute('aria-expanded', 'true');
accordionButton.setAttribute('aria-controls', `collapse-${sampleFrom}`);
accordionButton.textContent = `Samples ${sampleFrom} to ${sampleTo}`;
accordionCollapse.id = `collapse-${sampleFrom}`;
if (show) {
accordionCollapse.className = 'accordion-collapse collapse show';
} else {
accordionCollapse.className = 'accordion-collapse collapse';
}
// accordionCollapse.setAttribute('aria-labelledby', 'headingOne');
accordionCollapse.setAttribute('data-bs-parent', '#accordionSamples');
accordionBody.className = 'accordion-body';
accordionBody.id = `accordion-${sampleFrom}`;

// Append elements
accordionHeader.appendChild(accordionButton);
accordionItem.appendChild(accordionHeader);
accordionCollapse.appendChild(accordionBody);
accordionItem.appendChild(accordionCollapse);

return accordionItem;
}

function createDropdownItem(sampleName) {
const listItem = document.createElement("li");
const link = document.createElement("a");
link.className = "dropdown-item";
link.href = "#";
link.textContent = sampleName;
listItem.appendChild(link);
return listItem;
}

function populateDropdown(spec) {
const sampleSelector = document.getElementById("sampleSelectorElements");
let samples = [];
Object.entries(spec).forEach(
([_, specElement]) => {
let sampleIds = specElement["sample_ids"];
samples = [...samples, ...sampleIds];
}
);
console.log("All the samples:", samples);
for (let i = 0; i < samples.length; i++) {
const listItem = createDropdownItem(samples[i]);
sampleSelector.appendChild(listItem);
}
document.getElementById("sampleSelector").textContent = samples[0];
}

function findFromValue(dictionary, sampleId) {
for (let [key, value] of Object.entries(dictionary)) {
if (value.sample_ids.includes(sampleId)) {
return value.sample_counter.from;
}
}
return null; // return null if the sampleId is not found in any sample_ids array
}

function handleDropdownClick(event, spec, collapsibles) {
const sampleName = event.target.innerText; // Get value from dataset
const fromValue = findFromValue(spec, sampleName);

// Update the dropdown button text
document.getElementById("sampleSelector").textContent = sampleName;

// Hide all collapsibles
collapsibles.forEach(collapsible => {
collapsible.classList.remove("show");
});

// Show the selected collapsible (if a value is selected)
if (fromValue) {
const targetCollapsible = document.getElementById(`collapse-${fromValue}`);
targetCollapsible.classList.add('show');
}
}

$(document).ready(function () {
// temporary hack to make it look good with Bootstrap 5
adjustTagsToBS3()

const spec = JSON.parse(document.getElementById('vega_json').textContent);
let count = 0;
Object.entries(spec).forEach(([_, specElement]) => {
const sampleCounter = specElement["sample_counter"];
const sampleFrom = sampleCounter["from"];
const sampleTo = sampleCounter["to"];

if (count === 0) {
document.getElementById("accordionSamples")
.appendChild(createAccordionItem(sampleFrom, sampleTo, true));
} else {
document.getElementById("accordionSamples")
.appendChild(createAccordionItem(sampleFrom, sampleTo, false));
}
count += 1;

vegaEmbed(`#accordion-${sampleFrom}`, specElement["subcontext"]).then(
function (result) {
result.view.logLevel(vega.Warn);
window.v = result.view;
}
).catch(
function (error) {
handleErrors([error], $(`#accordion-${sampleFrom}`));
}
);
});

populateDropdown(spec);

const dropdownMenu = document.getElementById("sampleSelectorElements");
const collapsibles = document.querySelectorAll(".collapse.accordion-collapse");

// Add event listener to the dropdown menu (ul)
dropdownMenu.addEventListener('click', function(event) {
handleDropdownClick(event, spec, collapsibles);
});

const collapseAllButton = document.getElementById("collapseAllButton");
// Add event listener to thecollapse button
collapseAllButton.addEventListener('click', function(event) {
collapsibles.forEach(collapsible => {
collapsible.classList.remove("show");
});
});

});
</script>

{% endblock %}

{% block footer %}
{% set loading_selector = '#loading' %}
{% include 'js-error-handler.html' %}
{% endblock %}
Loading
Loading