The BaseSpace Report Builder allows you to hook into your output files and generate robust reports for the user. Reports can be created for any application you own in BaseSpace. If a report is not created for an application, the output files will be displayed in a file browser with a default report.
Any app can use the Reports Builder tool, whether is it a Web, Desktop, or Native app.
In the above example, the app has generated two AppResults. The app has the result
report enabled and the summary
report disabled, so you can see that two reports are visible, one result
report for each AppResult. If the summary
result was also enabled, it would be listed above the two result
reports.
The Reports Builder tool can by used by any apps and will generate reports using HTML, CSS, JS, and the Dotliquid languages.
Here is how a developer would first interact with the Reports Builder tool:
The app uploads result files to an AppResult while the developer is still developing the app
The developer creates a custom report for the app in the developer portal via the Reports Builder tool, where they can see a live preview of what the report will look like in the UI
The developer clicks Activate to activate a custom report for the app. All Completed AppSessions for the app are updated to reflect the new report in the BaseSpace UI
The developer or the user may now visit Complete
AppSessions (Analyses) in the UI to view Reports for the app
Note: In order to start using the Reports Builder, the app must have an AppSession that is in the Complete status.
After creating an application, simply navigate to "My Apps", select your app, click on "Reports Builder"
You will then be asked to select an AppSession for this app. Select an AppSession from the list with AppResult(s) that contain output files.
If you do not see any AppSessions in the list to select from, you will first have to create an output AppResult, upload files to that AppResult, and complete the AppSession tied to that AppResult. You will then see the AppSession display in this window.
You will be taken to the Active Report tab the first time that you visit the Reports Builder tab for an app, you will see a button labelled Start building custom reports. Below this section is a live preview of the report for your app currently. Since the app does not have a form yet, the preview will be a placeholder.
The active form is the form that the user will see in BaseSpace when they launch the app. Once you activate a form for your app, you can view that form directly on BaseSpace!
After clicking on Start building custom reports, you will be taken to the Revisions tab. From the Revisions tab, you can manage the Active Report for your app.
There are two reports available by default, the Default Report and the Example Report.
The Default Report is actually not a report. Instead, reports are not generated and the user is just shown the output file browser on the Analysis page.
The Example Report is a very basic example of a report that can be created using the Reports Builder. This report's purpose is to show developers a simple report to make it easier to create complex ones.
In addition to these reports, you will also see a few buttons on the Revisions tab.
Here's a quick description of each button's function:
Inactive
state, it can only be viewed in the Reports Builder tab. However, when a report is in the Active
state, it will be the report that any user will see when they launch the app from BaseSpace (if they have permission to view the app, of course.) Once a report is Active, it cannot be modified in the Reports Builder tool. Select a report that is not in the "Active" status and click "Open" to begin creating or editing the report.
A report is created for each AppSession that your app creates in BaseSpace. In addition, the output Report will always contain 'Files' if the app wrote data back to an AppResult in BaseSpace. Please use the Writing Back to BaseSpace Guide to write files back to BaseSpace. AppSessions should be marked as Complete
.
Once a report is "Active", it cannot be changed directly. To make changes to an "Active" report, simply copy the existing report, make changes to the copy, then "Activate" the new report. To make changes to an active report, simply select and Activate another report, then you should be able to edit the old report as needed until you Activate it once again.
Each report has two components, the Summary and the Result. The report is generated for every AppSession that your app has created.
One Summary Report is generated per AppSession. It is normally used to provide a general overview of all of the AppResults in the AppSession. A Summary Report is generated for every AppSession, even if the AppSession contains no AppResults.
One Result Report is generated for each AppResult in an AppSession. In general, the Result Report is used to give more detailed information and analysis for each AppResult in the AppSession. If an AppSession has no AppResults, then no Result Reports will be generated.
You can easily enable of disable both Summary and Result reports for your app. By default, both the Summary and Result reports are enabled. To disable or enable these reports for your application:
Reports are written using a conbination of HTML, CSS, JavaScript, Liquid, and some custom BaseSpace reporting syntax.
There are extensive examples of HTML, CSS, and Javascript online, so this guide will not go into the details of these languages. There are a few great resources available for free that cover these three languages in depth. W3Schools and Code Academy.
An easy way to get started with HTML and CSS is to use the popular Twitter Bootstrap library, which provides CSS classes and JavaScript plugins for common UI patterns. It also provides a convenient grid system for laying out page content. Combined with the DotLiquid syntax, the BaseSpace Report Builder provides a powerful tool for rendering informatics data in a user-friendly web interface.
The latest version of Twitter Bootstrap is available at http://getbootstrap.com, where you can learn how to use the CSS components and JavaScript plugins.
The BaseSpace-flavored variant of Twitter Bootstrap is hosted publicly. You can copy this HTML snippet into your templates for a quick BaseSpace look and feel:
<link href="https://da1s119xsxmu0.cloudfront.net/libraries/basestrap/1.0.0/css/master.min.css" rel="stylesheet" type="text/css" />
The latest version of Twitter Bootstrap itself (without the BaseSpace theme) is available at:
Liquid is a templating originally written by Shopify and is now open source in many languages including ruby and .NET. BaseSpace reports use the Liquid markup language for templating in the reports. The documentation for Liquid can be viewed here and here.
To enumerate a dictionary:
{% for key in mydictionary %}
The key is {{ key }}
The value is {{ mydictionary[key] }}
{% endfor %}
Accessing an array by index number is supported:
{{ myarray[4] }}
Object Filters
Filters can be applied to Liquid statements in the output reports.
Possible filters are:
find
: for xml files, if this filter is applied, it would take xpath expression and return the resultant xml text. Can be combined with capture
to save results.Example:
{% capture clusPF %}{{ statsFile | find: "/StatisticsAmplicon/OverallSamples/SummarizedSampleStatisics[SampleID='10002 - R1']/NumberOfClustersPF" }}{% endcapture %}
In the above example, the find
filter is applied within the capture
statement in Liquid, and is looking for a particular file.
stringify
: serializes the liquid object into json string. Does not work with all objects, please post on the google groups if there is an object you would like supported with stringify that is not already. Enables you to parse JSON exclusively within your reports.Example:
<script type="text/javascript">
var globals = {};
globals.sample = {{ sample | stringify }};
globals.sample.chromosomes = {{ sample.chromosomes | stringify }};
globals.sample.statsByChromosome = {{ statsFile.parse.StatisticsResequencing.Samples.SampleStatistics | stringify }};
</script>
The above statement will take the results returned from the javascript and stringify the sample.chromosomes
field which would likely be a list of chromosomes for that Sample. This object can now be parsed like normal JSON.
Dictionary Filters and Operators
The following are a suite of operators that allow filtering of the dictionary of files exposed in the Report Builder tool from your analysis' results. These can be applied on the name or directory of the files available.
Dictionary Filters/Operators:
where.starts_with
- returns the values where the dictionary key starts with provided stringwhere.ends_with
- returns the values where the dictionary key ends with provided stringwhere.contains
- returns the values where the dictionary key contains the provided stringfirst
- returns the first value, will throw an exceiption if nothing is therefirst_or_default
- returns the first or default value (usually null)Example 1:
The following example will return the first
file in the result that starts_with
datafile1.
{% assign_object datafile1 = result.files.where.starts_with["datafile1"].first %}
Example 2:
The following example will return the first_or_default
file that ends_with
.xml in the result.
{% assign_object anyXml = result.files.where.ends_with[".xml"].first_or_default %}
Example 3:
The following example will return the first
file in the result that contains
file3 anywhere in its name.
{% assign_object datafile3 = result.files.where.contains["file3"].first %}
Break and Continue Tags
The break
and continue
Liquid tags provide the ability to continue or break any loop in Liquid within the Report Builder.
For example:
{% for file in files %}
{% if file.href == null %}
{% continue %}
{% endif %}
{% if file.href == 'http://special.com' %}
{% assign specialHref = file.href %}
{% break %}
{% endif %}
{% endfor %}
The above statement would do the following:
for
loop), the logic would break one iteration through the for
loop if the specified condition (file.href == null
) occurred, then it would continue with the next iteration of the loop.break
statement is reached (if the file's href is http://special.com
), the break statement will exit this for
loop entirely and continue with the rest of the code outside of this loop if there is anyInterrupt Exception
If the condition in the interrupt_exception
statement is met, then BaseSpace will stop all execition of this report template and throw an error to the user.
Example:
The following example will throw an exception on the report if any file that ends with .csv has a size that is less than 1 byte
{% if result.files.where.ends_with[".csv"] | size < 1 %}
ERROR: no csv found for this result. Halting execution of template
{{ errors.interrupt_exception }}
{% endif %}
Built-in Variables:
result.id
To return Id of the AppResult
{{ result.id }}
result.name
To return Name of the AppResult
{{ result.name }}
result.session
To return Information about the AppSession that this AppResult is linked to
{{ result.session }}
result.samples
To return all of the Samples related to this AppResult
{{ result.samples }}
result.files
To return a list of all Files in an AppResult
{{ result.files }}
session.id
To return the Id of the AppSession
{{ session.id }}
session.name
The Name of the AppSession
{{ session.name }}
session.results
A list of all of the AppResults tied to this AppSession
{{ session.results }}
Collection of Samples
If Samples were specified as the input for an AppResult via the AppSession, the following variables are available:
result.samples[index]
The number of Samples that were used as input:
{{ result.samples[index] }}
where index is a 0-based integer
result.samples["sample_id"]
A list of Samples tied to the AppResult. sample_id
is the Sample Id as it appears on the Samplesheet:
{{ result.samples["sample_id"] }}
Accessing Sample Objects
samples["index"].id
The Id of the Sample at the specified index that was tied to the AppResult
To return the Id of the first Sample in the list:
{{ samples[0].id }}
samples["index"].name
The Name of the Sample at the specified index
To return the Name of the first Sample in the list:
{{ samples[0].name }}
samples["index"].sample_id
The Samplesheet Sample Id of the Sample at the specified index
To return the Samplesheet Sample Name of the first Sample in the list:
{{ samples[0].sample_id }}
samples["index"].status
The Status of the Sample
To return the Status of the first Sample in the list:
{{ samples[0].status }}
samples["index"].date_created
The date on which the Sample was created in UTC
To return the Date on which the first Sample in the list was created:
{{ samples[0].date_created }}
samples["index"].experiment_name
The name of the originating Run that created this Sample
To return the originating Run name of the first Sample in the list:
{{ samples[0].experiment_name }}
samples["index"].ispairedend
Whether or not this Sample has paired-end reads
To determine whether the first Sample in the list is paired end:
{{ samples[0].is_paired_end }}
samples["index"].read1
Number of read cycles for the first read
To return the number of read cycles for the first read of the first Sample in the list:
{{ samples[0].read1 }}
samples["index"].read2
Number of read cycles for the second read
To return the number of read cycles for the second read of the first Sample in the list:
{{ samples[0].read2 }}
samples["index"].numreadsraw
Number of bases that were read
To determine the number of bases read in the first Sample in the list:
{{ samples[0].num_reads_raw }}
samples["index"].numreadspf
Number of bases that were read that passed filter
To determine the number of bases read that passed filter in the first Sample in the list:
{{ samples[0].num_reads_pf }}
samples["index"].href
The URI to this Sample in BaseSpace
To return the Href of the first Sample in the list:
{{ samples[0].href }}
If a genome was provided for a sample, the following variables are available:
samples["index"].genome.name
The Name of this Sample's Genome, if provided
To return the Genome name of the first Sample in the list:
{{ samples[0].genome.name }}
samples["index"].genome.display_name
The Display Name for this Sample's Genome
To return the Genome display name of the first Sample in the list:
{{ samples[0].genome.display_name }}
samples["index"].genome.path
The Path of this Sample's Genome
To return the Genome's path in BaseSpace of the first Sample in the list:
{{ samples[0].genome.path }}
samples["index"].genome.source
The Source of this Sample's Genome
To return the Genome source of the first Sample in the list:
{{ samples[0].genome.source }}
samples["index"].genome.species
The Species of this Sample's Genome
To return the Genome species of the first Sample in the list:
{{ samples[0].genome.species }}
samples["index"].genome.build
The Build of this Sample's Genome
To return the Genome build of the first Sample in the list:
{{ samples[0].genome.build }}
result.files[index]
The index of the files in the AppResult. This will essentially show how many files are in the AppResult
{{ result.files["index"] }}
result.files[path]
The Path to the File in this AppResult in BaseSpace
{{ result.files["path"] }}
result.files[“myfile”].href
Get the URL to a file with .href like this:
{{ result.files["myfile"].href }}
result.files[“myfile”].content
Get the content of a file with .content like this:
{{ result.files["myfile"].content }}
result.files[“myfile”].parse
To access the data structure of a file, it must first be explicitly parsed like this:
{{ result.files["myfile"].parse }}
This is auto-determined by file extension:
.json file extension will be read in as a json file
.xml read in as xml file
.csv read in as csv file
.tab, .tsv read in as tab separated file
Before parsing, various options can be added to the file. These options can be chained in any order before parse. Options:
Noheader
skiplines
Json
Xml
Csv
Tsv
Noheader
Noheader must be used when a csv or tsv file has no header. Ex:
{{ result.files[“myfile.csv”].noheader.parse }}
Skiplines
Skiplines is used for files that have informational text at the beginning of the file which can cause parsing to fail. This will ignore the first n lines of the file for parsing.
{{ result.files[“myfile.csv”].skiplines[5].parse }}
Json, Xml, Csv, and Tsv
For cases where the extension doesn’t match the file type, like in .txt files, use:
For json: {{ result.files[“myfile”].json.parse }}
For xml: {{ result.files[“myfile”].xml.parse }}
For csv: {{ result.files[“myfile”].csv.parse }}
For tab separated: {{ result.files[“myfile”].tsv.parse }}
All of these options can be chained in any order before the parse:
{{ result.files[“myfile”].noheader.skiplines[2].csv.parse }}
{{ result.files[“myfile”].noheader.csv.skiplines[2].parse }}
{{ result.files[“myfile”].skiplines[2].csv.noheader.parse }}
Select Columns (csv only)
The select
statement allows the selection of specific columns in a .csv file by the column name or index.
The take
statement determines how many rows from the selected column(s) should be returned.
Example 1:
The following is an example of parsing the columns labelled 0
, 1
, and 2
to return the first value from each column
{% assign grid2 = result.files[key].select['0,1,2'].take[1].parse %}
Example 2:
The following is an example of parsing the columns labelled LastName
, City
, and Phone
to return the first 5 values from each column
{% assign grid3 = result.files[key].select['LastName','City','Phone'].take[5].parse %}
To Array (csv only)
The to_array
statement allows the output of data from a .csv file to a 2-dimensional data array
Example:
The following example will take the files that match the specified key
value and parse them into an array that is stringified into JSON
{{ result.files[key].parse.to_array | stringify }}
The output would look like JSON created from a .csv file.
Take (csv only)
The take
statement provides the ability to take a subset of rows from a .csv file
Example:
The following example will return the values from the first row in the first three columns:
{% assign grid2 = result.files[key].select['0,1,2'].take[1].parse %}
Example 1 Parsing an XML file
Assume that there is only one file named “testresultfolder/foo.xml” in the result with the following content:
<catalog>
<book id="bk101">
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
</book>
<book id="bk102">
<author>Ralls, Kim</author>
<title>Midnight Rain</title>
</book>
</catalog>
With Liquid template:
{% for filename in result.files %}
File path name: {{ filename }}
{% for book in result.files[filename].parse.catalog.book %}
Book Id: {{ book['@id'] }}
Author: {{ book.author }}
{% endfor %}
{% endfor %}
Note that result.files is a dictionary, with filename as key. Enumerating the dictionary returns the keys. Note that catalog in result.files[filename].catalog is an array, as can be seen in the XML Note that the book id is accessed like this: book[“@id”]. That is because it is an attribute as can be seen in the XML.
The output should be:
File path name: testresultfolder/foo.xml
Book Id: bk101
Author: Gambardella, Matthew
Book Id: bk102
Author: Ralls, Kim
This will also generate the same result:
{% for book in result.files[“testresultfolder/foo.xml”].parse.catalog.book %}
Book Id: {{ book['@id'] }}
Author: {{ book.author }}
{% endfor %}
If the foo.xml has a different extension, like foo.txt, this will also generate the same result:
{% for book in result.files[“testresultfolder/foo.txt”].xml.parse.catalog.book %}
Book Id: {{ book['@id'] }}
Author: {{ book.author }}
{% endfor %}
Example 2 csv files
Assume that there is only one file named “testresultfolder/bar.csv” in the result with the following content:
Title,Author
XML Developer's Guide, Matt
Midnight Rain, Kim
The following template:
{% for column_name in result.files[“testresultfolder/bar.csv”].parse.column_names %}
{{column_name}}
{% endfor %}
{% for row in result.files[“testresultfolder/bar.csv”].parse.rows %}
Title: {{ row[“Title”] }}
Author: {{ row[“Author”] }}
{% endfor %}
Will produce the following output:
Title
Author
XML Developer's Guide
Matt
Midnight Rain
Kim
The following template will generate the same output using column indexers:
{% for column_name in result.files[“testresultfolder/bar.csv”].parse.column_names %}
{{column_name}}
{% endfor %}
{% for row in result.files[“testresultfolder/bar.csv”].parse.rows %}
Title: {{ row[0] }}
Author: {{ row[1] }}
{% endfor %}
Example 3 csv files with informational text at the beginning of the file and txt extension
Assume that there is only one file named “testresultfolder/wibble.txt” in the result with the following content:
This file is generated by Book Tracker Extreme Pro Edition v14.5 gamma 3 build 92646
Title,Author
XML Developer's Guide, Matt
Midnight Rain, Kim
The following template:
{% for column_name in result.files[“testresultfolder/wibble.txt”].skiplines[1].csv.parse.column_names %}
{{column_name}}
{% endfor %}
{% for row in result.files[“testresultfolder/wibble.txt”].csv.skiplines[1].parse.rows %}
Title: {{ row[“Title”] }}
Author: {{ row[“Author”] }}
{% endfor %}
Will produce the following output:
Title
Author
XML Developer's Guide
Matt
Midnight Rain
Kim
Example 4 csv files with no header
Assume that there is only one file named “testresultfolder/baz.csv” in the result with the following content:
XML Developer's Guide, Matt
Midnight Rain, Kim
The following template:
{% for column_name in result.files[“testresultfolder/baz.csv”].noheader.parse.column_names %}
{{column_name}}
{% endfor %}
{% for row in result.files[“testresultfolder/baz.csv”].noheader.parse.rows %}
Title: {{ row[“column0”] }}
Author: {{ row[“column1”] }}
{% endfor %}
Will produce the following output:
column0
column1
XML Developer's Guide
Matt
Midnight Rain
Kim
The following template will generate the same output using column indexers:
{% for column_name in result.files[“testresultfolder/baz.csv”].noheader.parse.column_names %}
{{column_name}}
{% endfor %}
{% for row in result.files[“testresultfolder/bar.csv”].parse.rows %}
Title: {{ row[0] }}
Author: {{ row[1] }}
{% endfor %}
Example 5 JSON files
Assume that there is only one file in the output for the analysis called statisticsFile.json
and the contents of this file are the following:
{
SampleName:"104050_13",
MLST:{
SequenceType:"147",
AlleleType:{
gapA:"3",
infB:"4",
mdh:"6",
pgi:"1",
phoE:"7",
rpoB:"4",
tonB:"38"
},
Mismatches:"0",
Uncertainty:"-",
Depth:"35.7074285714",
MaxMAF:"0.0909090909091"
}
}
To return all of the keys for the AlleleType object in the JSON above, simply use the following:
{% for key in result.files[statisticsFile].Json.parse.MLST.AlleleType %}
<p>{{ key }}</p>
{% endfor %}
Which would return the following:
<p>gapA</p>
<p>infB</p>
<p>mdh</p>
<p>pgi</p>
<p>phoE</p>
<p>rpoB</p>
<p>tonB</p>
Then, to return the values for each of these keys we would use the following:
{% for key in result.files[statisticsFile].Json.parse.MLST.AlleleType %}
<p>{{ result.files[statisticsFile].Json.parse.MLST.AlleleType[key] }}</p>
{% endfor %}
This would return the following:
<p>3</p>
<p>4</p>
<p>6</p>
<p>1</p>
<p>7</p>
<p>4</p>
<p>38</p>
Ever wondered how we were able to create the charts that you see in BaseSpace? If you have not had a chance to view this already, check out the charts for the BacillusCereus Public Dataset in BaseSpace.
We use the d3.js (Data Driven Documents) Javascript charting library to dynamically create these charts from data in BaseSpace. D3 charts can take as input data from a file in the output AppResult or from another Web service.
For more information on D3 Charts, there is extensive documentation available on their website, which can be found here.
There are also some guides and tutorials available, here is an example of one that we recommend: Dashing D3.js.
This example report is the report for the Isaac native application in BaseSpace. The css files in the example are example css files, so the formatting may look different when previewing in your own report builder. Please considering using a public css library or creating your own.
Report UI
Code
Summary Report
This report does not contain a Summary report. Normally, a summary report would be shown above the Result report, but if it is empty it will not display.
Result Report
The Result report for this app is what is shown in the screenshot above, here is the code for the report:
<!-- begin header subtemplate -->
<!doctype html>
<html>
<head>
<!-- css includes -->
<link href="master.css" rel="stylesheet" />
<link href="slave.css" rel="stylesheet" />
<!-- JS Libraries -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="http://placeholder.url.com/jshashtable-2.1.js"></script>
<script src="http://placeholder.url.com/jquery.numberformatter-1.2.3.min.js"></script>
<script src="http://placeholder.url.com/underscore-min.js"></script>
<title>Report</title>
<style>
#coverage-histogram-container {
height: 320px;
}
</style>
</head>
<body>
<div class="container">
<!-- end header subtemplate -->
<div class="row-fluid">
<h1 class="col-span-12">
{% for sampleKey in result.samples %}
<a href="{{ result.samples[sampleKey].href }}"> {{ result.samples[sampleKey].name }}</a></h1>
{% endfor %}
</h1>
</div>
{% for key in result.files %}
{% if key contains 'ResequencingRunStatistics.xml' %}
{% assign totalReads = result.files[key].parse.StatisticsResequencing.OverallSamples.SummarizedSampleStatisics['NumberOfClustersPF'] %}
{% assign clustersAlignedR1 = result.files[key].parse.StatisticsResequencing.OverallSamples.SummarizedSampleStatisics['ClustersAlignedR1'] %}
{% assign clustersAlignedR2 = result.files[key].parse.StatisticsResequencing.OverallSamples.SummarizedSampleStatisics['ClustersAlignedR2'] %}
{% assign weightedCoverage = result.files[key].parse.StatisticsResequencing.RunStats.['OverallCoverage'] %}
{% assign fragmentLengthMedian = result.files[key].parse.StatisticsResequencing.OverallSamples.SummarizedSampleStatisics['FragmentLengthMedian']%}
{% assign fragmentLengthSD = result.files[key].parse.StatisticsResequencing.OverallSamples.SummarizedSampleStatisics['FragmentLengthSD'] %}
{% assign errorRateR1 = result.files[key].parse.StatisticsResequencing.OverallSamples.SummarizedSampleStatisics['ErrorRateR1'] %}
{% assign errorRateR2 = result.files[key].parse.StatisticsResequencing.OverallSamples.SummarizedSampleStatisics['ErrorRateR2'] %}
{% assign sNPsNumPassingVariants = result.files[key].parse.StatisticsResequencing.OverallSamples.SummarizedSampleStatisics.Variants.SNPs['NumPassingVariants'] %}
{% assign sNPHeterozygoteToHomozygoteRatio = result.files[key].parse.StatisticsResequencing.OverallSamples.SummarizedSampleStatisics.Variants.SNPs['HeterozygoteToHomozygoteRatio'] %}
{% assign sNPTransitionTransversionRatio = result.files[key].parse.StatisticsResequencing.OverallSamples.SummarizedSampleStatisics.Variants['TransitionTransversionRatio'] %}
{% assign variantsNumPassingVariants = result.files[key].parse.StatisticsResequencing.OverallSamples.SummarizedSampleStatisics.Variants.Insertions['NumPassingVariants'] %}
{% assign variantsHeterozygoteToHomozygoteRatio = result.files[key].parse.StatisticsResequencing.OverallSamples.SummarizedSampleStatisics.Variants.Insertions['HeterozygoteToHomozygoteRatio'] %}
{% assign deletionsNumPassingVariants = result.files[key].parse.StatisticsResequencing.OverallSamples.SummarizedSampleStatisics.Variants.Deletions['NumPassingVariants'] %}
{% assign deletionsHeterozygoteToHomozygoteRatio = result.files[key].parse.StatisticsResequencing.OverallSamples.SummarizedSampleStatisics.Variants.Deletions['HeterozygoteToHomozygoteRatio'] %}
{% if totalReads != 0 %}
{% assign percentAlignedR1 = clustersAlignedR1 | divided_by: totalReads | times: 100 %}
{% assign percentAlignedR2 = clustersAlignedR2 | divided_by: totalReads | times: 100 %}
{% else %}
{% assign percentAlignedR1 = "N/A" %}
{% assign percentAlignedR2 = "N/A" %}
{% endif %}
<div class="row-fluid">
<span class="col-span-6">
<section class="bs-panel">
<hgroup>
<h5>Alignment Statistics</h5>
</hgroup>
<table class="table bs-table bs-table-justify">
<tbody>
<tr>
<th>Number of reads</th>
<td>{{ totalReads }}</td>
</tr>
<tr>
<th>Coverage</th>
<td>{{ weightedCoverage }}</td>
</tr>
{% if result.samples[0].is_paired_end %}
<tr>
<th>Fragment Length Median</th>
<td>{{ fragmentLengthMedian }}</td>
</tr>
<tr>
<th>Fragment Length Standard Deviation</th>
<td>{{ fragmentLengthSD }}</td>
</tr>
{% endif %}
</tbody>
</table>
<table class="table bs-table bs-table-justify">
<thead>
<tr><th colspan="2">Read 1</th></tr>
</thead>
<tbody>
<tr>
<th>Aligned (%)</th>
<td>{{percentAlignedR1}}</td>
</tr>
<tr>
<th>Mismatch (%)</th>
<td>{{errorRateR1}}</td>
</tr>
</tbody>
</table>
{% if result.samples[0].is_paired_end %}
<table class="table bs-table bs-table-justify">
<thead>
<tr><th colspan="2">Read 2</th></tr>
</thead>
<tbody>
<tr>
<th>Aligned (%)</th>
<td>{{percentAlignedR2}}</td>
</tr>
<tr>
<th>Mismatch (%)</th>
<td>{{errorRateR2}}</td>
</tr>
</tbody>
</table>
{% endif %}
</section>
</span>
<span class="col-span-6">
<section class="bs-panel">
<hgroup>
<h5>Variant Statistics</h5>
</hgroup>
<table class="table bs-table bs-table-justify">
<thead>
<tr>
<th class="table-header" colspan="2">SNVs</th>
</tr>
</thead>
<tbody>
<tr>
<th>Total number</th>
<td>{{sNPsNumPassingVariants}}</td>
</tr>
<tr>
<th>Het/Hom Ratio</th>
<td>{{sNPHeterozygoteToHomozygoteRatio}}</td>
</tr>
<tr>
<th>Transitions / Transversions</th>
<td>{{sNPTransitionTransversionRatio}}</td>
</tr>
</tbody>
</table>
<table class="table bs-table bs-table-justify">
<thead>
<tr>
<th class="table-header" colspan="2">Insertions</th>
</tr>
</thead>
<tbody>
<tr>
<th>Total number</th>
<td>{{variantsNumPassingVariants}}</td>
</tr>
<tr>
<th>Het/Hom Ratio</th>
<td>{{variantsHeterozygoteToHomozygoteRatio}}</td>
</tr>
</tbody>
</table>
<table class="table bs-table bs-table-justify" >
<thead>
<tr>
<th class="table-header" colspan="2">Deletions</th>
</tr>
</thead>
<tbody>
<tr>
<th>Total number</th>
<td>{{deletionsNumPassingVariants}}</td>
</tr>
<tr>
<th>Het/Hom Ratio</th>
<td>{{deletionsHeterozygoteToHomozygoteRatio}}</td>
</tr>
</tbody>
</table>
</section>
</span>
</div>
{% endif %}
{% endfor %}
{% for key in result.files %}
{% if key contains 'CoverageHistogram.txt' %}
<br/>
<hr class="clearfix" />
<div class="row">
<span class="col-span-12">
<section class="bs-panel">
<hgroup>
<h5>Coverage Histogram</h5>
<div class="bs-toolbar">
<a href="{{ result.files[key].href }}" target="_blank"><label>Export (TSV)</label></a>
</div>
</hgroup>
<div class="padded clearfix">
<span class="col-span-2">
<select id="chr-select"></select>
</span>
<span class="col-span-2">
<input id="fix-scale" type="checkbox" /> <label>Fix Y Scale</label>
</span>
</div>
<div id="coverage-histogram-container">
<!-- d3 chart goes here -->
</div>
<div id="coverage-histogram-data" style="display:none">{{ result.files[key].content }}</div>
<script>
$(document).ready(function() {
parseCoverage("#coverage-histogram-container", $("#coverage-histogram-data").text());
});
</script>
</section>
</span>
</div>
<script>
<!-- d3 coverage chart -->
</script>
{% endif %}
{% endfor %}
<script>
$(document).ready(function() {
$(".bs-table td").each(function() {
var floatRegex = /\d+\./;
if ($(this).text().match(floatRegex)) {
$(this).formatNumber({format:"#,##0.00", locale:"us"});
}
});
});
</script>
</div>
</body>