Skip to content

Commit

Permalink
Refactor agg test builder to Vue for easier maintenance
Browse files Browse the repository at this point in the history
  • Loading branch information
abought committed Apr 5, 2019
1 parent c8d95c6 commit d8d10e3
Show file tree
Hide file tree
Showing 3 changed files with 208 additions and 224 deletions.
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
2,
"single",
{
"avoidEscape": true
"avoidEscape": true,
"allowTemplateLiterals": true
}
],
"linebreak-style": [
Expand Down
169 changes: 112 additions & 57 deletions examples/aggregation_tests.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,34 @@
<link href="//fonts.googleapis.com/css?family=Raleway:400,300,600" rel="stylesheet" type="text/css">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.css"/>

<!-- Dev build is "vue.js"; prod build is "vue.min.js" -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>

<!-- Necessary includes for LocusZoom.js -->
<script src="../dist/locuszoom.vendor.min.js" type="text/javascript"></script>
<script src="../dist/locuszoom.app.min.js" type="text/javascript"></script>
<link rel="stylesheet" href="../dist/locuszoom.css" type="text/css"/>

<!-- For interactive widgets specific to this page -->
<script type="application/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT" crossorigin="anonymous"></script>
<script type="application/javascript" src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js" integrity="sha384-Dziy8F2VlJQLMShA6FHWNul/veM9bCkRUaLqr199K94ntO5QUrLJBEbYegdSkkqX" crossorigin="anonymous"></script>

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tabulator/3.5.1/css/tabulator.min.css" integrity="sha384-uUckT6+9734bUz7HdTJ+c+PuMby2lpJoY32bbSz5AxNsX0HafyKd17e654uUdw1x" crossorigin="anonymous">
<script type="application/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/tabulator/3.5.1/js/tabulator.min.js" integrity="sha384-RpPuANG8/tgp6BYEwkavx0+frMPygzCmFcKd8bLQ4ReTROYaqH1h/mMgVLeA4cCX" crossorigin="anonymous"></script>
<script type="application/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT"
crossorigin="anonymous"></script>
<script type="application/javascript" src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"
integrity="sha384-Dziy8F2VlJQLMShA6FHWNul/veM9bCkRUaLqr199K94ntO5QUrLJBEbYegdSkkqX"
crossorigin="anonymous"></script>

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tabulator/3.5.1/css/tabulator.min.css"
integrity="sha384-uUckT6+9734bUz7HdTJ+c+PuMby2lpJoY32bbSz5AxNsX0HafyKd17e654uUdw1x" crossorigin="anonymous">
<script type="application/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/tabulator/3.5.1/js/tabulator.min.js"
integrity="sha384-RpPuANG8/tgp6BYEwkavx0+frMPygzCmFcKd8bLQ4ReTROYaqH1h/mMgVLeA4cCX"
crossorigin="anonymous"></script>

<!-- Code required to do the calculations (this treats the NPM package as a CDN and uses semver to automatically get the newest patch release) -->
<script type="application/javascript" src="https://unpkg.com/raremetal.js@~1.0.2/dist/raremetal.min.js" crossorigin="anonymous"></script>
<script type="application/javascript" src="https://unpkg.com/raremetal.js@~1.0.2/dist/raremetal.min.js"
crossorigin="anonymous"></script>

<!-- Helpers that use the external library to add functionality to LocusZoom -->
<script src="js/aggregation-test-helpers.js" type="application/javascript"></script>
<script src="js/aggregation-tests-example-page.js" type="application/javascript"></script>


<title>LocusZoom.js ~ Aggregation Tests</title>

Expand Down Expand Up @@ -75,13 +84,44 @@ <h3>Build your analysis</h3>
Specify the mask(s) and type(s) of aggregation test to run, for all genes in a specified plot region. Use
CTRL-click to select more than one mask or test.
</p>
<div id="widget-aggregation-container">
<div id="widget-aggregation-builder">Loading...</div>
<button id="button-aggregation-run" class="button-primary">Run tests</button>

<div id="widget-aggregation-builder">
<div>
<div class="row">
<div class="two columns" style="font-weight: bold;">Select phenotype</div>
<div class="ten columns">
<select v-model="selected_phenotype" style="min-width: 200px;">
<option v-for="(item, index) in phenotypes" :value="item.name" :key="index">{{ item.description }}
</option>
</select>
</div>
</div>
<div class="row">
<div class="two columns" style="font-weight: bold;">Select mask(s)</div>
<div class="ten columns">
<select size="5" multiple style="height: auto; min-width: 200px;" v-model="selected_masks">
<option v-for="(item, index) in masks" :value="item.name" :key="index">{{item.description}}</option>
</select>
</div>
</div>
<div class="row">
<div class="two columns" style="font-weight: bold;">Select test(s)</div>
<div class="ten columns">
<label style="display: inline;" v-for="test in calc_names">
<input type="checkbox" name="calc_choice" v-model="selected_tests" :id="test[0]" :value="test[0]">
<span class="label-body">{{ test[1] }}</span>
</label>
</div>
</div>
<div v-if="status_message" :style="status_css">{{ status_message }}</div>
<button class="button-primary" @click="runTests">Run tests</button>
</div>
</div>

</div>

<div class="row" id="section-results" style="display:none;"> <!-- This section is hidden until the first test is run -->
<div class="row" id="section-results" style="display:none;">
<!-- This section is hidden until the first test is run -->
<div class="twelve columns">
<h3>Interactive Plot</h3>
<p>
Expand All @@ -90,25 +130,28 @@ <h3>Interactive Plot</h3>

Each gene/interval can have more than one mask. The entire set of possible results in a given gene/interval is
summarized by color:
<span style="color:red">significant</span> / <span style="color:blue">not significant</span> / <span style="color:grey">no data available</span>.
<span style="color:red">significant</span> / <span style="color:blue">not significant</span> / <span
style="color:grey">no data available</span>.
</p>
<div id="lz-plot" class="lz-container-responsive"></div>

<h3>Table of aggregation test results</h3>
<div>
Currently reviewing results for group:
<span id="label-no-group-selected" style="color: grey;">(all genes in view)</span>
<span id="label-current-group-selected" style="color: #333333; display: none;"></span>
<span id="label-no-group-selected" style="color: grey;">(all genes in view)</span>
<span id="label-current-group-selected" style="color: #333333; display: none;"></span>

<p>Click on a row in the table below to see the variants associated with a specific mask + gene.</p>
<br>
<button id="download-aggregation" class="button-primary">Download</button><br>
<button id="download-aggregation" class="button-primary">Download</button>
<br>
<div id="results-table-aggregation"></div>
</div>

<h3>Variants in mask <span id="label-mask-selected"></span> </h3>
<h3>Variants in mask <span id="label-mask-selected"></span></h3>
<div>
<button id="download-variants" class="button-primary">Download</button><br>
<button id="download-variants" class="button-primary">Download</button>
<br>
<div id="results-table-variants"></div>
</div>
</div>
Expand All @@ -118,10 +161,13 @@ <h3>Variants in mask <span id="label-mask-selected"></span> </h3>
<h3>References</h3>
<ul>
<li>
Feng, S., Liu, D., Zhan, X., Wing, M. K., & Abecasis, G. R. (2014). RAREMETAL: fast and powerful meta-analysis for rare variants. Bioinformatics, 30(19), 2828-2829. (<a href="https://doi.org/10.1093/bioinformatics/btu367">link</a>)
Feng, S., Liu, D., Zhan, X., Wing, M. K., & Abecasis, G. R. (2014). RAREMETAL: fast and powerful meta-analysis
for rare variants. Bioinformatics, 30(19), 2828-2829. (<a href="https://doi.org/10.1093/bioinformatics/btu367">link</a>)
</li>
<li>
Liu, D. J., Peloso, G. M., Zhan, X., Holmen, O. L., Zawistowski, M., Feng, S. et al. (2014). Meta-analysis of gene-level tests for rare variant association. Nat Genet, 46(2), 200-204. (<a href="https://doi.org/10.1038/ng.2852">link</a>)
Liu, D. J., Peloso, G. M., Zhan, X., Holmen, O. L., Zawistowski, M., Feng, S. et al. (2014). Meta-analysis of
gene-level tests for rare variant association. Nat Genet, 46(2), 200-204. (<a
href="https://doi.org/10.1038/ng.2852">link</a>)
</li>
</ul>
</div>
Expand All @@ -134,6 +180,8 @@ <h3>References</h3>
</div>
</div>

<script src="js/aggregation-tests-example-page.js" type="application/javascript"></script>

<script type="application/javascript">
"use strict";

Expand All @@ -142,53 +190,60 @@ <h3>References</h3>
// masks is fetched from a server endpoint that returns simple metadata for this region
var WIDGET_SELECTOR_AGGREGATION = "#widget-aggregation-builder";
$.ajax("data/aggregation_masks.json") // For demo purposes. A real UI might provide a different way of defining masks.
.done(function(resp) {
.done(function (resp) {
// Flatten out the masks from all datasets into a single array, then load mask list into builder widget
var masks = [];
resp.data.forEach(function(dataset) {
var set_masks = dataset.masks.filter(function(item) { return item.groupType === "gene"; })
.map(function (item) { return [item.id, item.description]; });
// var pheno_categories = []; // labels
// var phenotypes = {}; // {label : pheno_details}
resp.data.forEach(function (dataset) { // TODO: Rewrite later to work with new payload structure
var set_masks = dataset.masks
.filter(function (item) {
return item.groupType.toLowerCase() === "gene";
}).map(function (item) {
return { name: item.id, description: item.description }
});
Array.prototype.push.apply(masks, set_masks);
});

// Populate the aggregation test builder UI
window.aggregationBuilder = new AggregationTestBuilder(WIDGET_SELECTOR_AGGREGATION, masks);
}).fail(function() {
$("#widget-aggregation-container").text("An error occurred while fetching available masks. Please reload the page.");
});
window.aggregationBuilder = makeUI(WIDGET_SELECTOR_AGGREGATION, masks, [{
name: 'apheno',
description: 'Sample phenotype'
}]);

window.aggregationBuilder.$on('run', function (options) {
// One time only: Create the plot and table widgets if they don't already exist
if (!window.plot || !(window.plot instanceof LocusZoom.Plot)) {
$("#section-results").css("display", "inherit"); // Hide results section until first use
createDisplayWidgets(window.selected_group); // Hack: func binds widget references to global scope
setupWidgetListeners(
window.plot, window.aggregationTable, window.variantsTable,
window.agg_calc_data, window.selected_group
);
}

// The "define calculations" widget should put the test definitions in a place that the data source
// will expect to find them- plot state. The widget should define `masks` and `calcs`.
// This design runs "all specified masks with all specified calcs", but more fine grained control could be
// added if needed.
window.plot.applyState({
aggregation_tests: options
}).then(function () {
aggregationBuilder.setStatus("Calculations complete", { color: "green" });
}).catch(function () {
aggregationBuilder.setStatus("An error occurred while running calculations");
});

})

}).fail(function () {
$(WIDGET_SELECTOR_AGGREGATION).text("An error occurred while fetching available masks. Please reload the page.");
});

// A bunch of stuff is clickable. Use observables to coordinate all the work of updating the UI across widgets
window.agg_calc_data = Observable(); // aggregation test results: redraw tables when new results found
window.selected_group = Observable(); // Identify the current selection, eg for labels & filters. (else `null`)

$("#button-aggregation-run").on("click", function() {
var valid = aggregationBuilder.validate();
if (valid) {
aggregationBuilder.setStatus("");

// One time only: Create the plot and table widgets if they don't already exist
if (!window.plot || !(window.plot instanceof LocusZoom.Plot)) {
$("#section-results").css("display", "inherit"); // Hide results section until first use
createDisplayWidgets(window.selected_group); // Hack: func binds widget references to global scope
setupWidgetListeners(
window.plot, window.aggregationTable, window.variantsTable,
window.agg_calc_data, window.selected_group
);
}

// The "define calculations" widget should put the test definitions in a place that the data source
// will expect to find them- plot state. The widget should define `masks` and `calcs`.
// This design runs "all specified masks with all specified calcs", but more fine grained control could be
// added if needed.
window.plot.applyState({
aggregation_tests: { masks: aggregationBuilder.getMasks(), calcs: aggregationBuilder.getCalcs() }
}).then(function() {
aggregationBuilder.setStatus("Calculations complete", { color: "green" });
}).catch(function() { aggregationBuilder.setStatus("An error occurred while running calculations"); });
} else {
aggregationBuilder.setStatus("One or more tests is invalid. Please select at least one mask and at least one test.");
}
});

</script>
</body>
</html>
Loading

0 comments on commit d8d10e3

Please sign in to comment.