diff --git a/Help/3 Analysis Modules/1 Basic Statistics.html b/Help/3 Analysis Modules/1 Basic Statistics.html index 76766aa..ff89044 100644 --- a/Help/3 Analysis Modules/1 Basic Statistics.html +++ b/Help/3 Analysis Modules/1 Basic Statistics.html @@ -1,7 +1,7 @@ -Basic Statistics +Basic statistics -

Basic Statistics

+

Basic statistics

Summary

-The Basic Statistics module generates some simple composition +The Basic statistics module generates some simple composition statistics for the file analysed.

@@ -21,14 +21,14 @@

Summary

  • File type: Says whether the file appeared to contain actual base calls or colorspace data which had to be converted to base calls
  • Encoding: Says which ASCII encoding of quality values was found in this -file. -
  • Total Sequences: A count of the total number of sequences processed. +file.
  • +
  • Total Sequences: A count of the total number of sequences processed. There are two values reported, actual and estimated. At the moment these will always be the same. In the future it may be possible to analyse just a subset of sequences and estimate the total number, to speed up the analysis, but since we have found that problematic sequences are not evenly distributed through a file we have disabled this for now.
  • -
  • Filtered Sequences: If running in Casava mode sequences flagged to be +
  • Filtered Sequences: If running in Casava mode sequences flagged to be filtered will be removed from all analyses. The number of such sequences removed will be reported here. The total sequences count above will not include these filtered sequences and will the number of sequences actually used for the @@ -41,12 +41,12 @@

    Summary

    Warning

    -Basic Statistics never raises a warning. +Basic statistics never raises a warning.

    Failure

    -Basic Statistics never raises an error. +Basic statistics never raises an error.

    Common reasons for warnings

    diff --git a/Help/3 Analysis Modules/10 Adapter Content.html b/Help/3 Analysis Modules/10 Adapter Content.html index 772cc96..625159f 100644 --- a/Help/3 Analysis Modules/10 Adapter Content.html +++ b/Help/3 Analysis Modules/10 Adapter Content.html @@ -1,7 +1,7 @@ -Adapter Content +Adapter content -

    Adapter Content

    +

    Adapter content

    Summary

    The Kmer Content module will do a generic analysis of all of the Kmers -in your library to find those which do not have even coverage through +in your library to find those which do not have even coverage through the length of your reads. This can find a number of different sources of bias in the library which can include the presence of read-through adapter sequences building up on the end of your sequences. @@ -26,15 +26,15 @@

    Summary

    be interested.

    -One obvious class of sequences which you might want to analyse are -adapter sequences. It is useful to know if your library contains a -significant amount of adapter in order to be able to assess whether -you need to adapter trim or not. Although the Kmer analysis can +One obvious class of sequences which you might want to analyse are +adapter sequences. It is useful to know if your library contains a +significant amount of adapter in order to be able to assess whether +you need to adapter trim or not. Although the Kmer analysis can theoretically spot this kind of contamination it isn't always clear. This module therefore does a specific search for a set of separately defined Kmers and will give you a view of the total proportion of your -library which contain these Kmers. A results trace will always be -generated for all of the sequences present in the adapter config file +library which contain these Kmers. A results trace will always be +generated for all of the sequences present in the adapter config file so you can see the adapter content of your library, even if it's low.

    @@ -48,7 +48,7 @@

    Summary

    In addition to classic adapter sequences the default configuration also includes polyA and polyG as sequences to search for. PolyA can be useful to include when looking at RNA-Seq libraries. PolyG is present as a -technical artefact in 2-colour illumina libraries where it is produced +technical artefact in 2-colour illumina libraries where it is produced when the signal from the cluster disappears. Both of these sequences are generally trimmed from the 3' end of sequences, and are therefore removed in a similar way to adapters, hence their inclusion in the default @@ -71,7 +71,7 @@

    Failure

    Common reasons for warnings

    Any library where a reasonable proportion of the insert sizes are shorter -than the read length will trigger this module. This doesn't indicate a +than the read length will trigger this module. This doesn't indicate a problem as such - just that the sequences will need to be adapter trimmed before proceeding with any downstream analysis.

    diff --git a/Help/3 Analysis Modules/4 Per Base Sequence Content.html b/Help/3 Analysis Modules/4 Per Base Sequence Content.html index bae1142..fc7bab3 100644 --- a/Help/3 Analysis Modules/4 Per Base Sequence Content.html +++ b/Help/3 Analysis Modules/4 Per Base Sequence Content.html @@ -27,11 +27,11 @@

    Summary

    It's worth noting that some types of library will always produce biased -sequence composition, normally at the start of the read. Libraries +sequence composition, normally at the start of the read. Libraries produced by priming using random hexamers (including nearly all RNA-Seq libraries) and those which were fragmented using transposases inherit an intrinsic -bias in the positions at which reads start. This bias does not concern -an absolute sequence, but instead provides enrichement of a number of +bias in the positions at which reads start. This bias does not concern +an absolute sequence, but instead provides enrichement of a number of different K-mers at the 5' end of the reads. Whilst this is a true technical bias, it isn't something which can be corrected by trimming and in most cases doesn't seem to adversely affect the downstream analysis. @@ -52,34 +52,35 @@

    Failure

    Common reasons for warnings

    -There are a number of common scenarios which would ellicit a warning +There are a number of common scenarios which would ellicit a warning or error from this module.

    1. Overrepresented sequences: If there is any evidence of overrepresented -sequences such as adapter dimers or rRNA in a sample then these sequences -may bias the overall composition and their sequence will emerge from this plot. +sequences such as adapter dimers or rRNA in a sample then these sequences +may bias the overall composition and their sequence will emerge from this plot.
    2. Biased fragmentation: Any library which is generated based on the ligation of random hexamers or through tagmentation should theoretically have good -diversity through the sequence, but experience has shown that these libraries +diversity through the sequence, but experience has shown that these libraries always have a selection bias in around the first 12bp of each run. This is due to a biased selection of random primers, but doesn't represent any individually biased sequences. Nearly all RNA-Seq libraries will fail this module because of -this bias, but this is not a problem which can be fixed by processing, and it -doesn't seem to adversely affect the ablity to measure expression. +this bias, but this is not a problem which can be fixed by processing, and it +doesn't seem to adversely affect the ablity to measure expression.
    3. Biased composition libraries: Some libraries are inherently biased in their -sequence composition. The most obvious example would be a library which has been +sequence composition. The most obvious example would be a library which has been treated with sodium bisulphite which will then have converted most of the cytosines -to thymines, meaning that the base composition will be almost devoid of cytosines +to thymines, meaning that the base composition will be almost devoid of cytosines and will thus trigger an error, despite this being entirely normal for that type of library
    4. -
    5. If you are analysing a library which has been aggressivley adapter trimmed -then you will naturally introduce a composition bias at the end of the reads as -sequences which happen to match short stretches of adapter are removed, leaving +
    6. If you are analysing a library which has been aggressivley adapter trimmed +then you will naturally introduce a composition bias at the end of the reads as +sequences which happen to match short stretches of adapter are removed, leaving only sequences which do not match. Sudden deviations in composition at the end of libraries which have undergone aggressive trimming are therefore likely to be spurious.
    7. +
    diff --git a/Help/3 Analysis Modules/6 Per Base N Content.html b/Help/3 Analysis Modules/6 Per Base N Content.html index 443a119..0c10209 100644 --- a/Help/3 Analysis Modules/6 Per Base N Content.html +++ b/Help/3 Analysis Modules/6 Per Base N Content.html @@ -13,7 +13,7 @@

    Per Base N Content

    Summary

    If a sequencer is unable to make a base call with sufficient confidence -then it will normally substitute an N rather than a conventional base] +then it will normally substitute an N rather than a conventional base call

    @@ -23,7 +23,7 @@

    Summary

    -It's not unusual to see a very low proportion of Ns appearing in a sequence, +It's not unusual to see a very low proportion of Ns appearing in a sequence, especially nearer the end of a sequence. However, if this proportion rises above a few percent it suggests that the analysis pipeline was unable to interpret the data well enough to make valid base calls. @@ -43,15 +43,15 @@

    Common reasons for warnings

    The most common reason for the inclusion of significant proportions of Ns is a general loss of quality, so the results of this module should be evaluated -in concert with those of the various quality modules. You should check the +in concert with those of the various quality modules. You should check the coverage of a specific bin, since it's possible that the last bin in this analysis -could contain very few sequences, and an error could be prematurely triggered in +could contain very few sequences, and an error could be prematurely triggered in this case.

    Another common scenario is the incidence of a high proportions of N at a small -number of positions early in the library, against a background of generally -good quality. Such deviations can occur when you have very biased sequence +number of positions early in the library, against a background of generally +good quality. Such deviations can occur when you have very biased sequence composition in the library to the point that base callers can become confused and make poor calls. This type of problem will be apparent when looking at the per-base sequence content results. diff --git a/Help/3 Analysis Modules/7 Sequence Length Distribution.html b/Help/3 Analysis Modules/7 Sequence Length Distribution.html index 27bbbb9..623b5e5 100644 --- a/Help/3 Analysis Modules/7 Sequence Length Distribution.html +++ b/Help/3 Analysis Modules/7 Sequence Length Distribution.html @@ -1,7 +1,7 @@ -Sequence Length Distribution +Sequence length distribution -

    Sequence Length Distribution

    +

    Sequence length distribution

    Summary

    Some high throughput sequencers generate sequence fragments of uniform length, but others can contain reads of wildly -varying lengths. Even within uniform length libraries some -pipelines will trim sequences to remove poor quality base calls +varying lengths. Even within uniform length libraries some +pipelines will trim sequences to remove poor quality base calls from the end.

    diff --git a/README.md b/README.md index ee8f15d..164c546 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,10 @@ -# FastQC +

    + + + + FastQC + +

    **A Quality Control application for FastQ files** @@ -23,7 +29,8 @@ FastQC is an application which takes a FastQ file and runs a series of tests on FastQC can be run either as an interactive graphical application which allows you to view results for multiple files in a single application. Alternatively you can run the program in a non interactive way (say as part of a pipeline) which will generate an HTML report for each file you process. FastQC is a cross-platform application, written in java. In theory it should run on any platform which has a suitable java runtime environment. -Having said that we've only tested in on Windows, MacOSX and Linux running the Oracle v1.6 to 1.8 JREs. Please let us know what happened if you try running it on other platforms / JREs. Please see the detailed instructions in the INSTALL.txt document to tell you how to get a suitable java version to run FastQC on your system. +Having said that we've only tested in on Windows, MacOSX and Linux running the Oracle v1.6 to 1.8 JREs. Please let us know what happened if you try running it on other platforms / JREs. +Please see the detailed instructions [in `INSTALL.md`](`INSTALL.md`) to tell you how to get a suitable java version to run FastQC on your system. ## Installation @@ -31,8 +38,15 @@ Please see the [**project web page**](http://www.bioinformatics.babraham.ac.uk/p ## Contributions -If you have any comments about FastQC we would like to hear them. You can either enter them into the github bug tracker at: +If you have any comments about FastQC we would like to hear them. +You can either create a [GitHub issue](https://github.com/s-andrews/FastQC/issues/) or send them directly to simon.andrews@babraham.ac.uk. -https://github.com/s-andrews/FastQC/issues/ +FastQC was written by Simon Andrews, at the Babraham Institute, Cambridge, UK. -..or send them directly to simon.andrews@babraham.ac.uk. +https://www.bioinformatics.babraham.ac.uk/ + + + + + Babraham Institute + diff --git a/Templates/Icons/error.png b/Templates/Icons/error.png deleted file mode 100644 index edb1fd8..0000000 Binary files a/Templates/Icons/error.png and /dev/null differ diff --git a/Templates/Icons/error.svg b/Templates/Icons/error.svg new file mode 100644 index 0000000..e0adc44 --- /dev/null +++ b/Templates/Icons/error.svg @@ -0,0 +1,3 @@ + + + diff --git a/Templates/Icons/fastqc_icon.png b/Templates/Icons/fastqc_icon.png deleted file mode 100644 index 348c93b..0000000 Binary files a/Templates/Icons/fastqc_icon.png and /dev/null differ diff --git a/Templates/Icons/fastqc_icon.svg b/Templates/Icons/fastqc_icon.svg new file mode 100644 index 0000000..618ccb4 --- /dev/null +++ b/Templates/Icons/fastqc_icon.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Templates/Icons/pass.svg b/Templates/Icons/pass.svg new file mode 100644 index 0000000..39f109f --- /dev/null +++ b/Templates/Icons/pass.svg @@ -0,0 +1,3 @@ + + + diff --git a/Templates/Icons/tick.png b/Templates/Icons/tick.png deleted file mode 100644 index 9c6a456..0000000 Binary files a/Templates/Icons/tick.png and /dev/null differ diff --git a/Templates/Icons/warning.png b/Templates/Icons/warning.png deleted file mode 100644 index 34db221..0000000 Binary files a/Templates/Icons/warning.png and /dev/null differ diff --git a/Templates/Icons/warning.svg b/Templates/Icons/warning.svg new file mode 100644 index 0000000..3023ec0 --- /dev/null +++ b/Templates/Icons/warning.svg @@ -0,0 +1,3 @@ + + + diff --git a/Templates/fastqc.css b/Templates/fastqc.css new file mode 100644 index 0000000..e839d6b --- /dev/null +++ b/Templates/fastqc.css @@ -0,0 +1,450 @@ +/* FastQC CSS Stylesheet */ + +/* RESET AND BASE STYLES */ + +* { + box-sizing: border-box; +} +html { + scroll-padding-top: 2rem; +} +body { + font-family: sans-serif; + color: #000; + background-color: #FFF; + border: 0; + margin: 0; + padding: 0; +} + +img { + max-width: 100%; +} + +p { + margin: 1rem 0; +} + +a { + color: #000080; + transition: color 0.2s ease; +} +a:hover { + color: #800000; +} + + +/* LAYOUT COMPONENTS */ + +.container { + display: flex; +} + +.sidebar { + flex: 0 0 300px; + background-color: #F8F8F8; + border-right: 1px solid #CCC; + position: sticky; + top: 0; + height: 100vh; + overflow-y: auto; +} + +.sidebar-header { + background-color: #EEE; + padding: 1rem; + border-bottom: 1px solid #CCC; +} + +.sidebar-header a { + text-decoration: none; + color: #000; +} + +.header-title { + margin: 0 0 0.5rem; +} +.header-title a { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.header-title svg { + height: 34px; + width: auto; +} + +.header-filename { + font-size: 0.85rem; +} + +.header-filename p { + margin: 0.25rem 0; +} + +.main { + padding: 0 2rem; + overflow-x: auto; + width: 100%; +} + +.module { + padding: 1rem 0 2rem; + overflow-x: auto; +} + +.footer { + margin-top: 3rem; + padding: 1.5rem 0; + border-top: 1px solid #EEE; + font-size: 0.9rem; + color: #666; +} + +/* NAVIGATION STYLES */ + +.navigation { + padding: 1rem 0; +} + +.navigation h2 { + margin: 0 0 1rem 1rem; + font-size: 1.1rem; +} + +.navigation ul { + padding: 0; + margin: 0; + list-style-type: none; +} +.navigation li { + padding: 0; + border-bottom: 1px solid #DDD; +} +.navigation li:last-child { + border-bottom: none; +} + +.navigation li a { + padding: 0.5rem 1rem; + transition: background-color 0.2s ease; + text-decoration: none; + color: #000; + font-size: 0.9rem; + display: flex; + align-items: center; + gap: 0.5rem; +} +.navigation li a:hover { + background-color: #F0F0F0; +} +.navigation li span { + display: inline-block; + border: 2px solid; + border-radius: 0.2rem; + font-size: 0.7rem; + padding: 0.1rem 0.3rem; + text-align: center; + min-width: 3.5rem; + text-transform: uppercase; + font-weight: bold; +} + +/* Status indicators */ +.sidebar-error { + background-color: #f0c6bc; + color: #862a13; + border-color: #862a13; +} + +.sidebar-warning { + background-color: #e8d28f; + color: #8d6c08; + border-color: #8d6c08; +} + +.sidebar-pass { + background-color: #afe0b7; + color: #2f6638; + border-color: #2f6638; +} + +/* CONTENT STYLING */ +h2 { + padding-bottom: 0.5rem; + margin-bottom: 1rem; + color: #800000; + border-bottom: 2px solid #800000; + display: flex; + align-items: center; + justify-content: space-between; +} +.module-title { + display: flex; + align-items: center; + gap: 0.5rem; +} + +table { + margin: 1rem 0; + border-collapse: collapse; + width: 100%; + min-width: 600px; +} + +th { + text-align: center; + background-color: #000080; + color: #FFF; + padding: 0.75rem 0.5rem; + font-weight: bold; +} + +td { + font-family: monospace; + background-color: #F5F5F5; + color: #000; + padding: 0.5rem; + border-bottom: 1px solid #DDD; +} +tr:nth-child(even) td { + background-color: #EEE; +} + +/* Basic statistics table styling (first module) */ +.module:first-of-type table thead { + display: none; +} +.module:first-of-type table td { + font-family: sans-serif; + background-color: transparent; +} +.module:first-of-type table tbody td:first-child { + font-weight: bold; + color: #333; + text-align: right; + width: 12rem; +} +.module:first-of-type table tbody tr:last-child td { + border-bottom: none; +} + +/* HELP TOGGLE FUNCTIONALITY */ + +/* Hide the checkbox */ +.help-checkbox { + display: none; +} + +/* Style the label as a button */ +.help-toggle { + background-color: #3498db; + color: white; + border: none; + border-radius: 4px; + padding: 4px 8px; + font-size: 11px; + font-weight: normal; + cursor: pointer; + transition: background-color 0.3s ease; +} + +.help-toggle:hover { + background-color: #2980b9; +} + +/* Show/hide text logic */ +.help-hide, .help-checkbox:checked ~ h2 .help-toggle .help-show { + display: none; +} + +.help-checkbox:checked ~ h2 .help-toggle .help-hide { + display: inline; +} + +.help-box { + background-color: #f8f9fa; + border: 1px solid #dee2e6; + border-radius: 4px; + padding: 0 15px; + margin-bottom: 0; + font-size: 0.8rem; + line-height: 1.5; + max-height: 0; + overflow: hidden; + opacity: 0; + transition: max-height 0.3s ease-out, opacity 0.3s ease-out, padding 0.3s ease-out, margin 0.3s ease-out; +} + +/* Show help box when checkbox is checked */ +.help-checkbox:checked ~ .help-box { + max-height: 1000px; + opacity: 1; + padding-top: 15px; + padding-bottom: 15px; + margin: 10px 0 20px 0; +} + +.help-box h4 { + margin: 0.5rem 0 0; +} +.help-box h4:first-child { + margin-top: 0; +} + +.help-box p { + margin: 0 0 10px; +} + +.help-box ul { + margin: 0 0 10px; + padding-left: 20px; +} + +/* RESPONSIVE DESIGN - SMALL SCREENS */ + +.mobile-nav { + display: none; + background-color: #EEE; + padding: 0.75rem 1rem; + border-bottom: 1px solid #CCC; + align-items: center; + justify-content: space-between; + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1000; +} + +.menu-toggle-input { + display: none; +} + +.menu-toggle { + cursor: pointer; + padding: 0.25rem; + display: flex; + flex-direction: column; + gap: 3px; +} + +.hamburger { + width: 20px; + height: 2px; + background-color: #333; + transition: all 0.3s ease; +} + +.mobile-title { + font-size: 1rem; + display: flex; + align-items: center; + justify-content: flex-end; + gap: 0.25rem; + margin-right: 1rem; +} +.mobile-title svg { + height: 24px; + width: auto; +} + +.mobile-title-app { + font-weight: bold; +} + +.mobile-title-filename { + font-size: 0.9rem; + color: #666; + overflow: hidden; + text-overflow: ellipsis; +} + +@media (max-width: 768px) { + html { + scroll-padding-top: 60px; + } + .mobile-nav { + display: flex; + } + + .container { + margin-top: 45px; + } + .main { + padding: 0 1rem; + } + .sidebar { + position: fixed; + top: 45px; + left: 0; + right: 0; + width: 100%; + height: calc(100vh - 45px); + transform: translateY(-100%); + transition: transform 0.3s ease; + z-index: 999; + border: none; + } + .sidebar-header { + display: none; + } + + /* Show sidebar when menu is toggled - slide down from top */ + .menu-toggle-input:checked ~ .container .sidebar { + transform: translateY(0); + } + + /* Hamburger animation when menu is open */ + .menu-toggle-input:checked ~ .mobile-nav .hamburger:nth-child(1) { + transform: rotate(45deg) translate(3px, 3px); + } + .menu-toggle-input:checked ~ .mobile-nav .hamburger:nth-child(2) { + opacity: 0; + } + .menu-toggle-input:checked ~ .mobile-nav .hamburger:nth-child(3) { + transform: rotate(-45deg) translate(4px, -4px); + } + + table { + font-size: 0.85rem; + } + + th, td { + padding: 0.5rem 0.25rem; + } +} + + + +/* PRINT STYLES */ + +@media print { + img { + max-width: 100% !important; + page-break-inside: avoid; + } + + h2, h3 { + page-break-after: avoid; + } + + .sidebar-header { + background-color: #FFF; + } + + .mobile-nav { + display: none !important; + } + + .sidebar { + position: static !important; + transform: none !important; + width: auto !important; + } + + .container { + flex-direction: column !important; + } +} diff --git a/Templates/fastqc2fo.xsl b/Templates/fastqc2fo.xsl index 83f6817..6986387 100644 --- a/Templates/fastqc2fo.xsl +++ b/Templates/fastqc2fo.xsl @@ -48,9 +48,7 @@ - - - + diff --git a/Templates/header_template.html b/Templates/header_template.html deleted file mode 100644 index 7ffb43b..0000000 --- a/Templates/header_template.html +++ /dev/null @@ -1,186 +0,0 @@ - - @media screen { - div.summary { - width: 18em; - position:fixed; - top: 3em; - margin:1em 0 0 1em; - } - - div.main { - display:block; - position:absolute; - overflow:auto; - height:auto; - width:auto; - top:4.5em; - bottom:2.3em; - left:18em; - right:0; - border-left: 1px solid #CCC; - padding:0 0 0 1em; - background-color: white; - z-index:1; - } - - div.header { - background-color: #EEE; - border:0; - margin:0; - padding: 0.5em; - font-size: 200%; - font-weight: bold; - position:fixed; - width:100%; - top:0; - left:0; - z-index:2; - } - - div.footer { - background-color: #EEE; - border:0; - margin:0; - padding:0.5em; - height: 1.3em; - overflow:hidden; - font-size: 100%; - font-weight: bold; - position:fixed; - bottom:0; - width:100%; - z-index:2; - } - - img.indented { - margin-left: 3em; - } - } - - @media print { - img { - max-width:100% !important; - page-break-inside: avoid; - } - h2, h3 { - page-break-after: avoid; - } - div.header { - background-color: #FFF; - } - - } - - body { - font-family: sans-serif; - color: #000; - background-color: #FFF; - border: 0; - margin: 0; - padding: 0; - } - - div.header { - border:0; - margin:0; - padding: 0.5em; - font-size: 200%; - font-weight: bold; - width:100%; - } - - #header_title { - display:inline-block; - float:left; - clear:left; - } - #header_filename { - display:inline-block; - float:right; - clear:right; - font-size: 50%; - margin-right:2em; - text-align: right; - } - - div.header h3 { - font-size: 50%; - margin-bottom: 0; - } - - div.summary ul { - padding-left:0; - list-style-type:none; - } - - div.summary ul li img { - margin-bottom:-0.5em; - margin-top:0.5em; - } - - div.main { - background-color: white; - } - - div.module { - padding-bottom:1.5em; - padding-top:1.5em; - } - - div.footer { - background-color: #EEE; - border:0; - margin:0; - padding: 0.5em; - font-size: 100%; - font-weight: bold; - width:100%; - } - - - a { - color: #000080; - } - - a:hover { - color: #800000; - } - - h2 { - color: #800000; - padding-bottom: 0; - margin-bottom: 0; - clear:left; - } - - table { - margin-left: 3em; - text-align: center; - } - - th { - text-align: center; - background-color: #000080; - color: #FFF; - padding: 0.4em; - } - - td { - font-family: monospace; - text-align: left; - background-color: #EEE; - color: #000; - padding: 0.4em; - } - - img { - padding-top: 0; - margin-top: 0; - border-top: 0; - } - - - p { - padding-top: 0; - margin-top: 0; - } diff --git a/Templates/module_wrapper.html b/Templates/module_wrapper.html new file mode 100644 index 0000000..a44eeca --- /dev/null +++ b/Templates/module_wrapper.html @@ -0,0 +1,16 @@ +
    + +

    +
    + {{STATUS_ICON}}{{MODULE_NAME}} +
    + +

    +
    +{{HELP_CONTENT}} +
    +{{MODULE_CONTENT}} +
    diff --git a/Templates/report_template.html b/Templates/report_template.html new file mode 100644 index 0000000..f45933e --- /dev/null +++ b/Templates/report_template.html @@ -0,0 +1,61 @@ + + + + {{TITLE}} + + + + + + + + + +
    + +
    + {{FASTQC_ICON_SVG_MOBILE}} + FastQC: + {{FILENAME}} +
    +
    + +
    + + + + +
    +{{MODULE_CONTENT}} + + +
    +
    + + diff --git a/Templates/sidebar_item.html b/Templates/sidebar_item.html new file mode 100644 index 0000000..0f7b354 --- /dev/null +++ b/Templates/sidebar_item.html @@ -0,0 +1,5 @@ +
  • + + {{STATUS_TEXT}}{{MODULE_NAME}} + +
  • diff --git a/uk/ac/babraham/FastQC/Modules/AbstractQCModule.java b/uk/ac/babraham/FastQC/Modules/AbstractQCModule.java index bb26b9a..9267ba2 100644 --- a/uk/ac/babraham/FastQC/Modules/AbstractQCModule.java +++ b/uk/ac/babraham/FastQC/Modules/AbstractQCModule.java @@ -43,7 +43,6 @@ protected void simpleXhtmlReport(HTMLReportArchive report,String svgData, Buffe XMLStreamWriter xhtml = report.xhtmlStream(); xhtml.writeStartElement("p"); xhtml.writeEmptyElement("img"); - xhtml.writeAttribute("class", "indented"); if (FastQCConfig.getInstance().svg_output) { xhtml.writeAttribute("src", ImageToBase64.svgImageToBase64(svgData)); } @@ -51,27 +50,27 @@ protected void simpleXhtmlReport(HTMLReportArchive report,String svgData, Buffe xhtml.writeAttribute("src", ImageToBase64.imageToBase64(image)); } xhtml.writeAttribute("alt", alt); - + // if(svgData!=null){ // xhtml.writeAttribute("width",String.valueOf(img.getWidth())); // xhtml.writeAttribute("height",String.valueOf(img.getHeight())); // } - + xhtml.writeEndElement();//p } - + protected void writeDefaultImage (HTMLReportArchive report, String fileName, String imageTitle, int width, int height) throws IOException, XMLStreamException { ZipOutputStream zip = report.zipFile(); - - // Write out the svg version of the image + + // Write out the svg version of the image JPanel resultsPanel = getResultsPanel(); resultsPanel.setSize(width,height); resultsPanel.validate(); - + String svgFilename = fileName.replaceAll("\\.png$", ".svg"); zip.putNextEntry(new ZipEntry(report.folderName()+"/Images/"+svgFilename)); - + String svgData = SVGImageSaver.saveImage(resultsPanel, zip); zip.closeEntry(); @@ -83,30 +82,30 @@ protected void writeDefaultImage (HTMLReportArchive report, String fileName, Str resultsPanel.setDoubleBuffered(false); resultsPanel.addNotify(); resultsPanel.validate(); - + resultsPanel.print(g); g.dispose(); - + ImageIO.write(b, "PNG", zip); zip.closeEntry(); - + simpleXhtmlReport(report, svgData, b, imageTitle); } protected void writeSpecificImage (HTMLReportArchive report, JPanel resultsPanel, String fileName, String imageTitle, int width, int height) throws IOException, XMLStreamException { ZipOutputStream zip = report.zipFile(); - - // Write out the svg version of the image + + // Write out the svg version of the image resultsPanel.setSize(width,height); resultsPanel.validate(); - + String svgFilename = fileName.replaceAll("\\.png$", ".svg"); zip.putNextEntry(new ZipEntry(report.folderName()+"/Images/"+svgFilename)); - + String svgData = SVGImageSaver.saveImage(resultsPanel, zip); zip.closeEntry(); @@ -118,22 +117,22 @@ protected void writeSpecificImage (HTMLReportArchive report, JPanel resultsPanel resultsPanel.setDoubleBuffered(false); resultsPanel.addNotify(); resultsPanel.validate(); - + resultsPanel.print(g); g.dispose(); - + ImageIO.write(b, "PNG", zip); zip.closeEntry(); - + simpleXhtmlReport(report, svgData, b, imageTitle); } protected void writeTable(HTMLReportArchive report, TableModel table) throws IOException,XMLStreamException { writeXhtmlTable(report,table); - writeTextTable(report,table); + writeTextTable(report,table); } protected void writeXhtmlTable(HTMLReportArchive report, TableModel table) throws IOException,XMLStreamException { @@ -141,17 +140,17 @@ protected void writeXhtmlTable(HTMLReportArchive report, TableModel table) throw w.writeStartElement("table"); w.writeStartElement("thead"); w.writeStartElement("tr"); - + for (int c=0;c longestAdapter) longestAdapter = adapter.sequence().length(); } @@ -130,7 +130,7 @@ public JPanel getResultsPanel() { if (!calculated) calculateEnrichment(); - // System.err.println("Xlabels="+xLabels.length+" labels="+labels.length+" enrichments="+enrichments.length+" enrichments[0]="+enrichments[0].length+" groups="+groups.length); + // System.err.println("Xlabels="+xLabels.length+" labels="+labels.length+" enrichments="+enrichments.length+" enrichments[0]="+enrichments[0].length+" groups="+groups.length); return (new LineGraph(enrichments, 0, 100, "Position in read (bp)", labels, xLabels, "% Adapter")); @@ -145,10 +145,10 @@ public void processSequence(Sequence sequence) { // We need to be careful about making sure that a sequence is not only longer // than we've seen before, but also that the last position we could find a hit // is a positive position. - + // If the sequence is longer than it was then we need to expand the storage in // all of the adapter objects to account for this. - + if (sequence.getSequence().length() > longestSequence && sequence.getSequence().length() - longestAdapter > 0) { longestSequence = sequence.getSequence().length(); for (int a=0;a0) { @@ -295,7 +295,7 @@ public void makeReport(HTMLReportArchive report) throws IOException,XMLStreamExc } sb.append("\n"); } - + } } @@ -319,10 +319,10 @@ public void incrementCount (int position) { // incremented positions. Rely on the upstream code // having done the expansion correctly already. - ++positions[position]; + ++positions[position]; } - + public void expandLengthTo (int newLength) { long [] newPositions = new long[newLength]; for (int i=0;i= 1000000000) { @@ -174,21 +174,21 @@ else if (length >= 1000) { unit = " kbp"; } - + String rawLength = ""+length; char [] chars = rawLength.toCharArray(); - + int lastIndex = 0; - + // Go through until we find a dot (if there is one) for (int i=0;i 0 && chars[lastIndex] == '.') { for (int i=0;i<=lastIndex;i++) { finalChars[i] = chars[i]; } - + return new String(finalChars)+unit; } @@ -211,7 +211,7 @@ public void makeReport(HTMLReportArchive report) throws XMLStreamException,IOExc @SuppressWarnings("serial") private class ResultsTable extends AbstractTableModel { - + private String [] rowNames = new String [] { "Filename", "File type", @@ -221,17 +221,17 @@ private class ResultsTable extends AbstractTableModel { "Sequences flagged as poor quality", "Sequence length", "%GC", - }; - + }; + // Sequence - Count - Percentage public int getColumnCount() { return 2; } - + public int getRowCount() { return rowNames.length; } - + public Object getValueAt(int rowIndex, int columnIndex) { switch (columnIndex) { case 0: return rowNames[rowIndex]; @@ -250,21 +250,21 @@ public Object getValueAt(int rowIndex, int columnIndex) { else { return minLength+"-"+maxLength; } - - - case 7 : + + + case 7 : if (aCount+tCount+gCount+cCount > 0) { return ""+(((gCount+cCount)*100)/(aCount+tCount+gCount+cCount)); } else { return 0; } - + } } return null; } - + public String getColumnName (int columnIndex) { switch (columnIndex) { case 0: return "Measure"; @@ -272,16 +272,16 @@ public String getColumnName (int columnIndex) { } return null; } - + public Class getColumnClass (int columnIndex) { switch (columnIndex) { case 0: return String.class; case 1: return String.class; } return null; - + } } - + } diff --git a/uk/ac/babraham/FastQC/Modules/DuplicationLevel.java b/uk/ac/babraham/FastQC/Modules/DuplicationLevel.java index 60db9dd..c6309b1 100644 --- a/uk/ac/babraham/FastQC/Modules/DuplicationLevel.java +++ b/uk/ac/babraham/FastQC/Modules/DuplicationLevel.java @@ -39,11 +39,11 @@ public class DuplicationLevel extends AbstractQCModule { private double percentDifferentSeqs = 0; private String [] labels; private static final DecimalFormat df = new DecimalFormat("#.##"); - + protected DuplicationLevel (OverRepresentedSeqs overrepresentedModule) { this.overrepresentedModule = overrepresentedModule; } - + public String description() { return "Plots the number of sequences which are duplicated to different levels"; } @@ -54,27 +54,27 @@ public boolean ignoreFilteredSequences() { } return false; } - + public boolean ignoreInReport () { if (ModuleConfig.getParam("duplication", "ignore") > 0) { return true; } return false; } - + protected synchronized void calculateLevels () { - + if (totalPercentages != null) return; - + totalPercentages = new double[16]; - + HashMap collatedCounts = new HashMap(); - + Iterator it = overrepresentedModule.sequences.keySet().iterator(); - + while (it.hasNext()) { int thisCount = overrepresentedModule.sequences.get(it.next()); - + if (collatedCounts.containsKey(thisCount)) { collatedCounts.put(thisCount,collatedCounts.get(thisCount)+1); } @@ -82,39 +82,39 @@ protected synchronized void calculateLevels () { collatedCounts.put(thisCount,1); } } - + // Now we can correct each of these - + HashMap correctedCounts = new HashMap(); - + Iterator itr = collatedCounts.keySet().iterator(); - + while (itr.hasNext()) { int dupLevel = itr.next(); int count = collatedCounts.get(dupLevel); - + correctedCounts.put(dupLevel,getCorrectedCount(overrepresentedModule.countAtUniqueLimit, overrepresentedModule.count, dupLevel, count)); } - + // From the corrected counts we can now work out the raw and deduplicated proportions - + double dedupTotal = 0; double rawTotal = 0; Iterator itc = correctedCounts.keySet().iterator(); - + while (itc.hasNext()) { int dupLevel = itc.next(); double count = correctedCounts.get(dupLevel); - + dedupTotal += count; rawTotal += count * dupLevel; int dupSlot = dupLevel - 1; - - // The dupSlot < 0 is a kludge to fix a problem we see if we have - // a duplication level > 2^31. It it gets bigger than 2^64 then + + // The dupSlot < 0 is a kludge to fix a problem we see if we have + // a duplication level > 2^31. It it gets bigger than 2^64 then // we're really stuffed but I think this will work for all practical // purposes and this is a really corner case anyway. if (dupSlot > 9999 || dupSlot < 0) dupSlot = 15; @@ -125,13 +125,13 @@ protected synchronized void calculateLevels () { else if (dupSlot > 49) dupSlot = 10; else if (dupSlot > 9) dupSlot = 9; - + totalPercentages[dupSlot] += count * dupLevel; - + } - + // System.err.println("True total = "+overrepresentedModule.count+" inferred total is "+rawTotal+" dedup total is "+dedupTotal); - + labels = new String [16]; for (int i=0;i 0) { return true; } return false; } - + private synchronized void calculateDistribution () { int maxLen = 0; int minLen = -1; max = 0; - + // Find the min and max lengths for (int i=0;i0) { @@ -69,18 +69,18 @@ private synchronized void calculateDistribution () { maxLen = i; } } - + // We can get a -1 value for min if there aren't any valid sequences // at all. - + if (minLen < 0) minLen = 0; - + // We put one extra category either side of the actual size if (minLen>0) minLen--; maxLen++; - + int [] startAndInterval = getSizeDistribution(minLen, maxLen); - + // Work out how many categories we need int categories = 0; int currentValue = startAndInterval[0]; @@ -88,19 +88,19 @@ private synchronized void calculateDistribution () { ++categories; currentValue+= startAndInterval[1]; } - + graphCounts = new double[categories]; xCategories = new String[categories]; - + for (int i=0;i maxLen) { maxValue = maxLen; } - + for (int bp=minValue;bp<=maxValue;bp++) { if (bp < lengthCounts.length) { graphCounts[i] += lengthCounts[bp]; @@ -113,7 +113,7 @@ private synchronized void calculateDistribution () { else { xCategories[i] = minValue+"-"+maxValue; } - + if (graphCounts[i] > max) max = graphCounts[i]; } calculated = true; @@ -129,31 +129,31 @@ public void processSequence(Sequence sequence) { } lengthCounts = newLengthCounts; } - + ++lengthCounts[seqLen]; - + } - + private int [] getSizeDistribution (int min, int max) { - + // We won't group if they've asked us not to if (FastQCConfig.getInstance().nogroup) { return(new int [] {min,1}); } - + int base = 1; - + while (base > (max-min)) { base /= 10; } - + int interval; int starting; int [] divisions = new int [] {1,2,5}; - + OUTER: while (true) { - + for (int d=0;d helpFileMapping; public HTMLReportArchive (SequenceFile sequenceFile, QCModule [] modules, File htmlFile) throws IOException, XMLStreamException { this.sequenceFile = sequenceFile; this.modules = modules; this.htmlFile = htmlFile; this.zipFile = new File(htmlFile.getAbsoluteFile().toString().replaceAll("\\.html$", "")+".zip"); - StringWriter htmlStr = new StringWriter(); + + // Load the HTML template + this.htmlTemplate = loadTemplate("/Templates/report_template.html"); + + // Initialize help file mapping + this.helpFileMapping = initializeHelpFileMapping(); + + // Create XMLStreamWriter for module content + this.moduleContentWriter = new StringWriter(); XMLOutputFactory xmlfactory = XMLOutputFactory.newInstance(); - this.xhtml= xmlfactory.createXMLStreamWriter(htmlStr); - - + this.xhtml= xmlfactory.createXMLStreamWriter(moduleContentWriter); + + zip = new ZipOutputStream(new FileOutputStream(zipFile)); zip.putNextEntry(new ZipEntry(folderName()+"/")); zip.putNextEntry(new ZipEntry(folderName()+"/Icons/")); zip.putNextEntry(new ZipEntry(folderName()+"/Images/")); - startDocument(); - for (int m=0;m>"); - data.append(modules[m].name()); - data.append("\t"); - if (modules[m].raisesError()) { - data.append("fail"); - } - else if (modules[m].raisesWarning()) { - data.append("warn"); - } - else { - data.append("pass"); - } - data.append("\n"); - xhtml.writeEndElement(); - modules[m].makeReport(this); - data.append(">>END_MODULE\n"); - - xhtml.writeEndElement(); - } - closeDocument(); - - zip.putNextEntry(new ZipEntry(folderName()+"/fastqc_report.html")); + // Initialize data document + data.append("##FastQC\t"); + data.append(FastQCApplication.VERSION); + data.append("\n"); + + // Generate module content using template-based approach + String moduleContent = generateModuleContent(); + + // Close XMLStreamWriter (no longer needed for main content) xhtml.flush(); xhtml.close(); - zip.write(htmlStr.toString().getBytes()); + + // Generate final HTML from template + String finalHtml = generateHtmlFromTemplate(moduleContent); + + zip.putNextEntry(new ZipEntry(folderName()+"/fastqc_report.html")); + zip.write(finalHtml.getBytes()); zip.closeEntry(); zip.putNextEntry(new ZipEntry(folderName()+"/fastqc_data.txt")); zip.write(data.toString().getBytes()); zip.closeEntry(); - + + // Generate summary.txt + String summaryText = generateSummaryText(); + zip.putNextEntry(new ZipEntry(folderName()+"/summary.txt")); + zip.write(summaryText.getBytes()); + zip.closeEntry(); + //XSL-FO try { DocumentBuilderFactory domFactory=DocumentBuilderFactory.newInstance(); domFactory.setNamespaceAware(false); DocumentBuilder builder=domFactory.newDocumentBuilder(); - Document src=builder.parse(new InputSource( new StringReader(htmlStr.toString()))); + Document src=builder.parse(new InputSource( new StringReader(finalHtml))); InputStream rsrc=getClass().getResourceAsStream("/Templates/fastqc2fo.xsl"); if(rsrc!=null) { @@ -157,7 +135,7 @@ else if (modules[m].raisesWarning()) { builder=domFactory.newDocumentBuilder(); Document html2fo=builder.parse(rsrc); rsrc.close(); - + TransformerFactory tf=TransformerFactory.newInstance(); Templates templates=tf.newTemplates(new DOMSource(html2fo)); zip.putNextEntry(new ZipEntry(folderName()+"/fastqc.fo")); @@ -168,19 +146,18 @@ else if (modules[m].raisesWarning()) { catch (Exception e) { e.printStackTrace(); } - - + + zip.close(); - + // Save the HTML file at the same level as the zip file - PrintWriter pr = new PrintWriter(new FileWriter(htmlFile)); - - pr.print(htmlStr.toString()); - + + pr.print(finalHtml); + pr.close(); - + if (FastQCConfig.getInstance().do_unzip) { unzipZipFile(zipFile); if (FastQCConfig.getInstance().delete_after_unzip) { @@ -188,18 +165,18 @@ else if (modules[m].raisesWarning()) { } } } - + private void unzipZipFile (File file) throws IOException { ZipFile zipFile = new ZipFile(file); Enumeration entries = zipFile.entries(); int size; byte [] buffer = new byte[1024]; - + while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); - + // System.out.println("Going to extract '"+entry.getName()+"'"); - + if (entry.isDirectory()) { File dir = new File(file.getParent()+"/"+entry.getName()); if (dir.exists() && dir.isDirectory()) continue; // Don't need to do anything @@ -217,196 +194,256 @@ private void unzipZipFile (File file) throws IOException { bos.close(); bis.close(); } - + zipFile.close(); } - public XMLStreamWriter xhtmlStream () - { + public XMLStreamWriter xhtmlStream (){ return this.xhtml; - } - + } + public StringBuffer dataDocument() { return data; } - + public String folderName () { return htmlFile.getName().replaceAll("\\.html$", ""); } - + public ZipOutputStream zipFile () { return zip; } - - private void startDocument () throws IOException,XMLStreamException - { - - // Just put the fastQC version at the start of the text report - data.append("##FastQC\t"); - data.append(FastQCApplication.VERSION); - data.append("\n"); - - // Add in the icon files for pass/fail/warn - for(String icnName:new String[]{ - "fastqc_icon.png", - "warning.png", - "error.png", - "tick.png"}) - { - InputStream in =getClass().getResourceAsStream("/Templates/Icons/"+icnName); - if(in==null) continue; - zip.putNextEntry(new ZipEntry(folderName()+"/Icons/"+icnName)); - int len; - while ((len = in.read(buffer)) > 0) { - zip.write(buffer, 0, len); - } - in.close(); - zip.closeEntry(); + + private String loadTemplate(String templatePath) throws IOException { + InputStream templateStream = getClass().getResourceAsStream(templatePath); + StringWriter templateWriter = new StringWriter(); + byte[] buffer = new byte[1024]; + int nRead; + while ((nRead = templateStream.read(buffer)) != -1) { + templateWriter.write(new String(buffer, 0, nRead)); + } + templateStream.close(); + return templateWriter.toString(); + } + + private String generateSummaryItems() throws IOException { + StringBuffer summaryItems = new StringBuffer(); + String sidebarItemTemplate = loadTemplate("/Templates/sidebar_item.html"); + + for (int m=0;m"); - xhtml.writeStartElement("html"); - xhtml.writeStartElement("head"); - - xhtml.writeStartElement("title"); - xhtml.writeCharacters(sequenceFile.name()); - xhtml.writeCharacters(" FastQC Report"); - xhtml.writeEndElement();//title - - InputStream rsrc=getClass().getResourceAsStream("/Templates/header_template.html"); - if(rsrc!=null) - { - xhtml.writeStartElement("style"); - xhtml.writeAttribute("type", "text/css"); - - byte array[]=new byte[128]; - int nRead; - while((nRead=rsrc.read(array))!=-1) { xhtml.writeCharacters(new String(array,0,nRead));} - rsrc.close(); - xhtml.writeEndElement();//style - } - - - - - xhtml.writeEndElement();//head - - xhtml.writeStartElement("body"); - - xhtml.writeStartElement("div"); - xhtml.writeAttribute("class", "header"); - - xhtml.writeStartElement("div"); - xhtml.writeAttribute("id", "header_title"); - - xhtml.writeEmptyElement("img"); - xhtml.writeAttribute("src", base64ForIcon("Icons/fastqc_icon.png")); - xhtml.writeAttribute("alt", "FastQC"); - xhtml.writeCharacters("FastQC Report"); - xhtml.writeEndElement();//div - - xhtml.writeStartElement("div"); - xhtml.writeAttribute("id", "header_filename"); - xhtml.writeCharacters(df.format(new Date())); - xhtml.writeEmptyElement("br"); - xhtml.writeCharacters(sequenceFile.name()); - xhtml.writeEndElement();//div - xhtml.writeEndElement();//div - - - xhtml.writeStartElement("div"); - xhtml.writeAttribute("class", "summary"); - - xhtml.writeStartElement("h2"); - xhtml.writeCharacters("Summary"); - xhtml.writeEndElement();//h2 - - - xhtml.writeStartElement("ul"); - + String item = sidebarItemTemplate; + item = item.replace("{{MODULE_INDEX}}", String.valueOf(m)); + item = item.replace("{{MODULE_NAME}}", modules[m].name()); + + if (modules[m].raisesError()) { + item = item.replace("{{STATUS_CLASS}}", "sidebar-error"); + item = item.replace("{{STATUS_TEXT}}", "Error"); + } else if (modules[m].raisesWarning()) { + item = item.replace("{{STATUS_CLASS}}", "sidebar-warning"); + item = item.replace("{{STATUS_TEXT}}", "Warn"); + } else { + item = item.replace("{{STATUS_CLASS}}", "sidebar-pass"); + item = item.replace("{{STATUS_TEXT}}", "Pass"); + } + + summaryItems.append(item).append("\n"); + } + + return summaryItems.toString(); + } + + private String generateSummaryText() { StringBuffer summaryText = new StringBuffer(); - + for (int m=0;m>"); + data.append(modules[m].name()); + data.append("\t"); + if (modules[m].raisesError()) { + data.append("fail"); + } else if (modules[m].raisesWarning()) { + data.append("warn"); + } else { + data.append("pass"); + } + data.append("\n"); + + // Let the module generate its content + modules[m].makeReport(this); + data.append(">>END_MODULE\n"); + + // Close the module's XMLStreamWriter + moduleXhtml.flush(); + moduleXhtml.close(); + + // Restore the original XMLStreamWriter + this.xhtml = originalXhtml; + + // Apply the module wrapper template + String moduleWrapper = moduleWrapperTemplate; + moduleWrapper = moduleWrapper.replace("{{MODULE_INDEX}}", String.valueOf(m)); + moduleWrapper = moduleWrapper.replace("{{MODULE_NAME}}", modules[m].name()); + moduleWrapper = moduleWrapper.replace("{{STATUS_ICON}}", getStatusIcon(modules[m])); + moduleWrapper = moduleWrapper.replace("{{HELP_CONTENT}}", extractHelpText(modules[m].name())); + moduleWrapper = moduleWrapper.replace("{{MODULE_CONTENT}}", moduleBodyWriter.toString()); + + allModulesContent.append(moduleWrapper).append("\n"); } + + return allModulesContent.toString(); + } + + private String generateHtmlFromTemplate(String moduleContent) throws IOException { + SimpleDateFormat df = new SimpleDateFormat("EEE d MMM yyyy"); + + String html = htmlTemplate; + html = html.replace("{{TITLE}}", sequenceFile.name() + " FastQC Report"); + html = html.replace("{{CSS_CONTENT}}", loadTemplate("/Templates/fastqc.css")); + html = html.replace("{{DATE}}", df.format(new Date())); + html = html.replace("{{FILENAME}}", sequenceFile.name()); + html = html.replace("{{FASTQC_ICON_SVG_MOBILE}}", getFastQCIconWithUniqueIds("mobile")); + html = html.replace("{{FASTQC_ICON_SVG_SIDEBAR}}", getFastQCIconWithUniqueIds("sidebar")); + html = html.replace("{{SUMMARY_ITEMS}}", generateSummaryItems()); + html = html.replace("{{MODULE_CONTENT}}", moduleContent); + html = html.replace("{{VERSION}}", FastQCApplication.VERSION); + + return html; + } + + + + + + + /** + * Initialize mapping between module names and their corresponding help file paths + */ + private Map initializeHelpFileMapping() { + Map mapping = new HashMap(); + mapping.put("Basic statistics", "/Help/3 Analysis Modules/1 Basic statistics.html"); + mapping.put("Per base sequence quality", "/Help/3 Analysis Modules/2 Per Base Sequence Quality.html"); + mapping.put("Per sequence quality scores", "/Help/3 Analysis Modules/3 Per Sequence Quality Scores.html"); + mapping.put("Per base sequence content", "/Help/3 Analysis Modules/4 Per Base Sequence Content.html"); + mapping.put("Per sequence GC content", "/Help/3 Analysis Modules/5 Per Sequence GC Content.html"); + mapping.put("Per base N content", "/Help/3 Analysis Modules/6 Per Base N Content.html"); + mapping.put("Sequence length distribution", "/Help/3 Analysis Modules/7 Sequence length distribution.html"); + mapping.put("Sequence duplication levels", "/Help/3 Analysis Modules/8 Duplicate Sequences.html"); + mapping.put("Overrepresented sequences", "/Help/3 Analysis Modules/9 Overrepresented Sequences.html"); + mapping.put("Adapter content", "/Help/3 Analysis Modules/10 Adapter content.html"); + mapping.put("Kmer Content", "/Help/3 Analysis Modules/11 Kmer Content.html"); + mapping.put("Per tile sequence quality", "/Help/3 Analysis Modules/12 Per Tile Sequence Quality.html"); + return mapping; } - - private void closeDocument () throws XMLStreamException - { - xhtml.writeEndElement();//div - xhtml.writeStartElement("div"); - xhtml.writeAttribute("class", "footer"); - xhtml.writeCharacters("Produced by "); - xhtml.writeStartElement("a"); - xhtml.writeAttribute("href", "http://www.bioinformatics.babraham.ac.uk/projects/fastqc/"); - xhtml.writeCharacters("FastQC"); - xhtml.writeEndElement();//a - xhtml.writeCharacters(" (version "+FastQCApplication.VERSION+")"); - xhtml.writeEndElement();//div - - xhtml.writeEndElement();//body - xhtml.writeEndElement();//html + + /** + * Extract text content from help HTML files, removing images and HTML tags + */ + private String extractHelpText(String moduleName) { + String helpFilePath = helpFileMapping.get(moduleName); + if (helpFilePath == null) { + return "

    Help documentation not available for this module.

    "; } - - - - + + try { + String helpHtml = loadTemplate(helpFilePath); + return convertHelpHtmlToText(helpHtml); + } catch (IOException e) { + return "

    Help documentation could not be loaded for this module.

    "; + } + } + + /** + * Convert help HTML to clean text content, removing images and preserving structure + */ + private String convertHelpHtmlToText(String htmlContent) { + // Remove the HTML document structure, head, and body tags + String content = htmlContent.replaceAll("(?s)", ""); + content = content.replaceAll("(?s).*?", ""); + content = content.replaceAll("(?s)]*>", ""); + content = content.replaceAll("(?s).*?", ""); + + // Remove all img tags and p tags containing only images + content = content.replaceAll("(?s)

    \\s*]*>\\s*

    ", ""); + content = content.replaceAll("(?s)]*>", ""); + + // Remove empty p tags + content = content.replaceAll("(?s)

    \\s*

    ", ""); + + // Remove h1 tags completely since they duplicate the module name + content = content.replaceAll("(?s)]*>.*?", ""); + + // Convert h2 tags to h4 for better integration + content = content.replaceAll("(?s)]*)>", ""); + content = content.replaceAll("(?s)", ""); + + // Clean up extra whitespace and newlines + content = content.replaceAll("\\n\\s*\\n\\s*\\n", "\n\n"); + content = content.replaceAll("(?m)^\\s+", ""); + content = content.trim(); + + return content; + } + } diff --git a/uk/ac/babraham/FastQC/Report/stylesheet.txt b/uk/ac/babraham/FastQC/Report/stylesheet.txt deleted file mode 100644 index f52e99e..0000000 --- a/uk/ac/babraham/FastQC/Report/stylesheet.txt +++ /dev/null @@ -1,55 +0,0 @@ - body { - font-family: sans-serif; - color: #000000; - background-color: #FFFFFF; - } - - a { - color: #000080; - } - - a:hover { - color: #800000; - } - - h2 { - color: #800000; - padding-bottom: 0; - margin-bottom: 0; - padding-top: 2em; - } - - table { - margin-left: 3em; - text-align: center; - } - - th { - text-align: center; - background-color: #000080; - color: #FFFFFF; - padding: 0.4em; - } - - td { - font-family: monospace; - text-align: left; - background-color: #EEEEEE; - color: #000000; - padding: 0.4em; - } - - img { - padding-top: 0; - margin-top: 0; - border-top: 0; - } - - img.indented { - margin-left: 3em; - } - - p { - padding-top: 0; - margin-top: 0; - } diff --git a/uk/ac/babraham/FastQC/Resources/babraham.svg b/uk/ac/babraham/FastQC/Resources/babraham.svg new file mode 100644 index 0000000..882e504 --- /dev/null +++ b/uk/ac/babraham/FastQC/Resources/babraham.svg @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uk/ac/babraham/FastQC/Resources/babraham_darkbg.svg b/uk/ac/babraham/FastQC/Resources/babraham_darkbg.svg new file mode 100644 index 0000000..bf6602d --- /dev/null +++ b/uk/ac/babraham/FastQC/Resources/babraham_darkbg.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uk/ac/babraham/FastQC/Resources/fastqc_icon.png b/uk/ac/babraham/FastQC/Resources/fastqc_icon.png index 348c93b..fb7b2cb 100644 Binary files a/uk/ac/babraham/FastQC/Resources/fastqc_icon.png and b/uk/ac/babraham/FastQC/Resources/fastqc_icon.png differ diff --git a/uk/ac/babraham/FastQC/Resources/fastqc_icon.svg b/uk/ac/babraham/FastQC/Resources/fastqc_icon.svg index 04bf1d5..618ccb4 100644 --- a/uk/ac/babraham/FastQC/Resources/fastqc_icon.svg +++ b/uk/ac/babraham/FastQC/Resources/fastqc_icon.svg @@ -1,189 +1,47 @@ - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - C - - - - - - - Q - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uk/ac/babraham/FastQC/Resources/fastqc_icon_100.png b/uk/ac/babraham/FastQC/Resources/fastqc_icon_100.png index 668c917..43e0a8e 100644 Binary files a/uk/ac/babraham/FastQC/Resources/fastqc_icon_100.png and b/uk/ac/babraham/FastQC/Resources/fastqc_icon_100.png differ diff --git a/uk/ac/babraham/FastQC/Resources/fastqc_icon_hires.png b/uk/ac/babraham/FastQC/Resources/fastqc_icon_hires.png new file mode 100644 index 0000000..a77b12e Binary files /dev/null and b/uk/ac/babraham/FastQC/Resources/fastqc_icon_hires.png differ diff --git a/uk/ac/babraham/FastQC/Resources/fastqc_logo.png b/uk/ac/babraham/FastQC/Resources/fastqc_logo.png new file mode 100644 index 0000000..f66b429 Binary files /dev/null and b/uk/ac/babraham/FastQC/Resources/fastqc_logo.png differ diff --git a/uk/ac/babraham/FastQC/Resources/fastqc_logo.svg b/uk/ac/babraham/FastQC/Resources/fastqc_logo.svg new file mode 100644 index 0000000..75fa6ea --- /dev/null +++ b/uk/ac/babraham/FastQC/Resources/fastqc_logo.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uk/ac/babraham/FastQC/Resources/fastqc_logo_darkbg.png b/uk/ac/babraham/FastQC/Resources/fastqc_logo_darkbg.png new file mode 100644 index 0000000..79b3864 Binary files /dev/null and b/uk/ac/babraham/FastQC/Resources/fastqc_logo_darkbg.png differ diff --git a/uk/ac/babraham/FastQC/Resources/fastqc_logo_darkbg.svg b/uk/ac/babraham/FastQC/Resources/fastqc_logo_darkbg.svg new file mode 100644 index 0000000..0f1ecaf --- /dev/null +++ b/uk/ac/babraham/FastQC/Resources/fastqc_logo_darkbg.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +