From 00df6091355947944f2435af064a4e3e9f6ed93d Mon Sep 17 00:00:00 2001 From: kohleman <kohleman> Date: Thu, 29 Aug 2013 14:05:14 +0000 Subject: [PATCH] initial commit for complete Flow Cell statistics SVN: 29692 --- .../as/webapps/cellStatistics/html/index.html | 248 ++++++++++++++++++ .../as/webapps/cellStatistics/html/style.css | 47 ++++ .../webapps/cellStatistics/plugin.properties | 6 + 3 files changed, 301 insertions(+) create mode 100644 deep_sequencing_unit/source/core-plugins/laneStatistics/1/as/webapps/cellStatistics/html/index.html create mode 100644 deep_sequencing_unit/source/core-plugins/laneStatistics/1/as/webapps/cellStatistics/html/style.css create mode 100644 deep_sequencing_unit/source/core-plugins/laneStatistics/1/as/webapps/cellStatistics/plugin.properties diff --git a/deep_sequencing_unit/source/core-plugins/laneStatistics/1/as/webapps/cellStatistics/html/index.html b/deep_sequencing_unit/source/core-plugins/laneStatistics/1/as/webapps/cellStatistics/html/index.html new file mode 100644 index 00000000000..06d40af202c --- /dev/null +++ b/deep_sequencing_unit/source/core-plugins/laneStatistics/1/as/webapps/cellStatistics/html/index.html @@ -0,0 +1,248 @@ +<!DOCTYPE html> +<html> + <head> + <title>Flowcell Statistics</title> + <script type="text/javascript" src="/openbis/resources/js/d3.v3.min.js"></script> + <script type="text/javascript" src="/openbis/resources/js/d3.layout.js"></script> + <script type="text/javascript" src="/openbis/resources/js/jquery.js"></script> + <script type="text/javascript" src="/openbis/resources/js/openbis.js"></script> + <script type="text/javascript" src="/openbis/resources/js/openbis-dsu.js"></script> + <script type="text/javascript" src="/openbis/resources/js/openbis-action-deferrer.js"></script> + <script type="text/javascript" src="/openbis/resources/js/FileSaver.js"></script> + <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0-rc1/css/bootstrap.min.css"> + <link rel="stylesheet" type="text/css" href="style.css" /> + </head> + <body> + <script> + + var formatThousands = d3.format(","); + dsu = new openbis_dsu('/openbis/openbis', '/datastore_server'); + var webAppContext = new openbisWebAppContext(); + dsu.server.useSession(webAppContext.getSessionId()); + + var prop; + var flowcellProperties = []; + var myArray = []; + var sumArray = []; + var sumsArray = [] + + loadData() + + /* ------- General functions -------------------------------------------*/ + + // used for sorting by lane of the final array of objects + function compare(a,b) { + if (a.lane < b.lane) + return -1; + if (a.lane > b.lane) + return 1; + return 0; + } + + function isNumber(n) { + return !isNaN(parseFloat(n)) && isFinite(n); + } + + function roundFloat(myFloat) { + return Number(myFloat).toFixed(2); + } + + + /* -------------------------------------------------------------------*/ + +function loadData () { + var dataSetProperties; + var prop; + + dsu.retrieveSample(webAppContext.getEntityIdentifier(), function(response) { + + flowcellProperties = response.result[0].properties; + numberOfLanes = (flowcellProperties.LANECOUNT); + + var sampleCodes = []; + for (i = 1; i <= numberOfLanes; i++) { + sampleCodes.push(webAppContext.entityIdentifier + ":" + i); + } + + var deferrer = new openbisActionDeferrer(function(){ + sumArray.sort(compare); + plotLaneTable (sumArray, true); + downloadButton ("tableStats"); + $("#save_as" + "tableStats").click(function() { submit_download_form("html", "tableStats"); }); + }, sampleCodes); + + sampleCodes.forEach(function(sampleCode){ + dsu.retrieveDataSetsForSample(sampleCode, function(response){ + getProperties(response); + //console.log("got response " + sampleCode); + var sums = calculateSum(myArray, sampleCode, true); + //console.log(sums[0]) + sumArray.push(sums[0]); + myArray = []; + deferrer.dependencyCompleted(sampleCode); + }); + }); + + function getProperties(dataSetProperties) { + for (var i = 0; i < dataSetProperties.result.length; i++) { + myArray.push({ + 'externalSampleName' : dataSetProperties.result[i].properties.EXTERNAL_SAMPLE_NAME, + 'index1': dataSetProperties.result[i].properties.BARCODE, + 'index2': dataSetProperties.result[i].properties.INDEX2, + 'percFilteringPass' : parseFloat(dataSetProperties.result[i].properties.PERCENTAGE_PASSED_FILTERING), + 'rawReadsSum' : dataSetProperties.result[i].properties.RAW_READS_SUM, + 'pfReadsSum' : dataSetProperties.result[i].properties.PF_READS_SUM, + 'rawYieldMbases' : dataSetProperties.result[i].properties.RAW_YIELD_MBASES, + 'yieldMbases' : dataSetProperties.result[i].properties.YIELD_MBASES, + 'percRawClustersPerLane': parseFloat(dataSetProperties.result[i].properties.PERCENTAGE_RAW_CLUSTERS_PER_LANE), + 'pfMeanQualityScore' : parseFloat(dataSetProperties.result[i].properties.PFMEANQUALITYSCORE), + 'pfYieldq30Percentage' : parseFloat(dataSetProperties.result[i].properties.PFYIELDQ30PERCENTAGE) + }); + }; + }; + }); +}; + +function plotLaneTable (sumArray, withNOINDEX) { + + var laneTableStats = d3.select("body") + .append("table") + .attr("id", "tableStats") + .attr("class", "tableStats") + ; + header = {"Lane":1, "# of Samples":1, "Average Passed Filtering (PF)":1, "Sum Raw Reads":1, "Sum PF Reads":1, "Sum Raw Bases":1, + "Sum PF Bases":1, "Average Raw Clusters in % per Index":1, "Average PF Phred Score":1, "Average > 30 Phred Score":1 } + + // create the table header + var thead = laneTableStats.selectAll("th") + .data(d3.keys(header)) + .enter().append("th") + .text(function(d){return d}) + ; + + // create rows + var tr = laneTableStats.selectAll("tr") + .data(sumArray).enter().append("tr") + // cells + var td = tr.selectAll("td") + .data(function(d){return d3.values(d)}) + .enter().append("td") + .text(function(d) {if (isNumber(d)) {return formatThousands(d)}; return d;}) + .style("text-align", function(d){if (isNumber(d)) {return "right"} return "left"}) + ; +} + +function placeholder() { + +var space = d3.select("body") + .append("svg") + .attr("id", "placeholder") + .attr("width", 1500) + .attr("height", 50) + ; +} + +function calculateSum(statisticsArray, sampleCode, withNOINDEX) { + + var sums = [] + var averagePercFilteringPass = 0; + var sumRawReads = 0; + var sumPfReads = 0; + var sumRawYieldMbases = 0; + var sumYieldMbases = 0; + var averagePercRawClustersPerLane = 0; + var averagePfMeanQualityScore = 0; + var averagePfYieldq30Percentage = 0; + + for (var i = 0; i < statisticsArray.length; i++) { + // do not calculate with the NOINDEX reads + if ((typeof (statisticsArray[i].externalSampleName) == 'undefined') && withNOINDEX) { + continue; + } else { + + averagePercFilteringPass = averagePercFilteringPass + parseFloat(statisticsArray[i].percFilteringPass); + sumRawReads = sumRawReads + parseInt(statisticsArray[i].rawReadsSum); + sumPfReads = sumPfReads + parseInt(statisticsArray[i].pfReadsSum); + sumRawYieldMbases = sumRawYieldMbases + parseInt(statisticsArray[i].rawYieldMbases); + sumYieldMbases = sumYieldMbases + parseInt(statisticsArray[i].yieldMbases); + averagePercRawClustersPerLane = averagePercRawClustersPerLane + parseInt(statisticsArray[i].percRawClustersPerLane); + averagePfMeanQualityScore = averagePfMeanQualityScore + parseInt(statisticsArray[i].pfMeanQualityScore); + averagePfYieldq30Percentage = averagePfYieldq30Percentage + parseInt(statisticsArray[i].pfYieldq30Percentage); + } + } + if (withNOINDEX) {penalty = -1} else {penalty = 0} + + // added the Math.max function to make sure that there is no division by zero, + // can happen when a single sample is on a lane (no multiplexing) + averagePercFilteringPass = roundFloat(averagePercFilteringPass / Math.max((statisticsArray.length + penalty),1)); + averagePercRawClustersPerLane = roundFloat(averagePercRawClustersPerLane / Math.max((statisticsArray.length + penalty),1)); + averagePfMeanQualityScore = roundFloat(averagePfMeanQualityScore / Math.max((statisticsArray.length + penalty),1)); + averagePfYieldq30Percentage = roundFloat(averagePfYieldq30Percentage / Math.max((statisticsArray.length + penalty),1)); + + sums.push({ + 'lane' : sampleCode.split("/")[2].split(":")[1], + 'numberOfSamples' : statisticsArray.length, + 'averagePercFilteringPass' : averagePercFilteringPass, + 'rawReadsSum' : sumRawReads, + 'pfReadsSum' : sumPfReads, + 'rawYieldMbases' : sumRawYieldMbases, + 'yieldMbases' : sumYieldMbases, + 'percRawClustersPerLane': averagePercRawClustersPerLane, + 'pfMeanQualityScore' : averagePfMeanQualityScore, + 'pfYieldq30Percentage' : averagePfYieldq30Percentage + }); + return sums; +} + + + function downloadButton (buttonName) { + + var div = d3.select("body").append("button") + .attr("class", "btn-xs") + .attr("type", "submit") + .attr("id", "save_as" + buttonName) + .attr("value", "") + .text("Save") + ; + } + + function submit_download_form(output_format, svgName) + { + var rawSampleName = webAppContext.entityIdentifier + var split = rawSampleName.split(":") + var fc = split[0].split("/")[2] + var lane = split[1] + + // Get the d3js SVG element + var tmp = document.getElementById(svgName); + + if (output_format == "svg") { + + // Extract the data as SVG text string + var svg_xml = "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">" + (new XMLSerializer).serializeToString(tmp) + "</svg>"; + + var blob = new Blob([svg_xml], {type: "image/svg+xml;charset=utf-8"}); + } + if (output_format == "html") { + var html = "<html> <head> <title></title> <style type=\"text/css\">" + + "table.tableStats { font-family: sans-serif; font-size: 14px; border-collapse:collapse; }" + + ".tableStats th { padding:6px 10px; color:#444; font-weight:bold; text-shadow:1px 1px 1px #fff; border-bottom:2px solid #444; }" + + ".tableStats tr:nth-child(even) { background: WhiteSmoke; }" + + ".tableStats td { padding:0px 10px 10px 10px; }" + + "</style> </head> <body>" + + (new XMLSerializer).serializeToString(tmp) + + "</body> </html>" + var blob = new Blob([html], {type: "image/html;charset=utf-8"}); + } + if (output_format == "png") { + console.log("png") + } + + saveAs(blob, fc + "_" + svgName + "." + output_format); + + } + + </script> + </body> +</html> + diff --git a/deep_sequencing_unit/source/core-plugins/laneStatistics/1/as/webapps/cellStatistics/html/style.css b/deep_sequencing_unit/source/core-plugins/laneStatistics/1/as/webapps/cellStatistics/html/style.css new file mode 100644 index 00000000000..985d8ac6db0 --- /dev/null +++ b/deep_sequencing_unit/source/core-plugins/laneStatistics/1/as/webapps/cellStatistics/html/style.css @@ -0,0 +1,47 @@ + .axis path, + .axis line { + fill: none; + stroke: #000; + shape-rendering: crispEdges; + } + + .axis text { + font-family: sans-serif; + font-size: 10px; + } + + table.tableStats { + font-family: sans-serif; + font-size: 14px; + border-collapse:collapse; + } + + .tableStats th { + padding:6px 10px; + color:#444; + font-weight:bold; + text-shadow:1px 1px 1px #fff; + border-bottom:2px solid #444; + } + + .tableStats tr:nth-child(even) { + background: WhiteSmoke; + } + .tableStats td { + + padding:0px 10px 10px 10px; + + } + + div.tooltip { + position: absolute; + text-align: center; + width: 100px; + height: 42px; + padding: 2px; + font: 12px sans-serif; + background: LightSalmon; + border: 0px; + border-radius: 8px; + pointer-events: none; + } diff --git a/deep_sequencing_unit/source/core-plugins/laneStatistics/1/as/webapps/cellStatistics/plugin.properties b/deep_sequencing_unit/source/core-plugins/laneStatistics/1/as/webapps/cellStatistics/plugin.properties new file mode 100644 index 00000000000..19629033996 --- /dev/null +++ b/deep_sequencing_unit/source/core-plugins/laneStatistics/1/as/webapps/cellStatistics/plugin.properties @@ -0,0 +1,6 @@ +# The properties file for an example webapps plugin +# This file has no properties defined because none need to be defined. +webapp-folder = html +openbisui-contexts = sample-details-view +sample-entity-types = ILLUMINA_FLOW_CELL +label = Flow Cell Statistics -- GitLab