sequenceserver 2.2.0 → 3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sequenceserver might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/COPYRIGHT.txt +1 -1
- data/bin/sequenceserver +4 -2
- data/lib/sequenceserver/blast/error.rb +53 -0
- data/lib/sequenceserver/blast/job.rb +2 -43
- data/lib/sequenceserver/job.rb +21 -11
- data/lib/sequenceserver/makeblastdb-modified-with-cache.rb +345 -0
- data/lib/sequenceserver/makeblastdb.rb +26 -12
- data/lib/sequenceserver/routes.rb +29 -3
- data/lib/sequenceserver/server.rb +1 -1
- data/lib/sequenceserver/version.rb +1 -1
- data/lib/sequenceserver.rb +3 -0
- data/public/404.html +27 -0
- data/public/config.js +0 -6
- data/public/css/grapher.css +1 -1
- data/public/css/sequenceserver.css +22 -11
- data/public/css/sequenceserver.min.css +2 -2
- data/public/js/circos.js +7 -3
- data/public/js/dnd.js +3 -3
- data/public/js/fastq_to_fasta.js +35 -0
- data/public/js/form.js +30 -11
- data/public/js/grapher.js +123 -113
- data/public/js/hit.js +8 -2
- data/public/js/hits_overview.js +4 -1
- data/public/js/jquery_world.js +0 -1
- data/public/js/kablammo.js +4 -0
- data/public/js/length_distribution.js +5 -1
- data/public/js/null_plugins/download_links.js +7 -0
- data/public/js/null_plugins/hit_buttons.js +11 -0
- data/public/js/null_plugins/report_plugins.js +18 -0
- data/public/js/query.js +26 -6
- data/public/js/report.js +33 -17
- data/public/js/search.js +0 -8
- data/public/js/sidebar.js +11 -1
- data/public/js/tests/mock_data/sequences.js +18 -1
- data/public/js/tests/search_query.spec.js +12 -3
- data/public/sequenceserver-report.min.js +76 -42
- data/public/sequenceserver-search.min.js +34 -33
- data/views/layout.erb +9 -12
- metadata +32 -23
    
        data/public/js/report.js
    CHANGED
    
    | @@ -8,15 +8,14 @@ import { ReportQuery } from './query'; | |
| 8 8 | 
             
            import Hit from './hit';
         | 
| 9 9 | 
             
            import HSP from './hsp';
         | 
| 10 10 | 
             
            import AlignmentExporter from './alignment_exporter';
         | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 11 | 
            +
            import ReportPlugins from 'report_plugins';
         | 
| 14 12 |  | 
| 15 13 | 
             
            /**
         | 
| 16 14 | 
             
             * Renders entire report.
         | 
| 17 15 | 
             
             *
         | 
| 18 16 | 
             
             * Composed of Query and Sidebar components.
         | 
| 19 17 | 
             
             */
         | 
| 18 | 
            +
             | 
| 20 19 | 
             
            class Report extends Component {
         | 
| 21 20 | 
             
                constructor(props) {
         | 
| 22 21 | 
             
                    super(props);
         | 
| @@ -47,15 +46,21 @@ class Report extends Component { | |
| 47 46 | 
             
                    this.prepareAlignmentOfSelectedHits = this.prepareAlignmentOfSelectedHits.bind(this);
         | 
| 48 47 | 
             
                    this.prepareAlignmentOfAllHits = this.prepareAlignmentOfAllHits.bind(this);
         | 
| 49 48 | 
             
                    this.setStateFromJSON = this.setStateFromJSON.bind(this);
         | 
| 49 | 
            +
                    this.plugins = new ReportPlugins(this);
         | 
| 50 50 | 
             
                }
         | 
| 51 | 
            +
             | 
| 51 52 | 
             
                /**
         | 
| 52 53 | 
             
               * Fetch results.
         | 
| 53 54 | 
             
               */
         | 
| 54 55 | 
             
                fetchResults() {
         | 
| 56 | 
            +
                    const path = location.pathname + '.json' + location.search;
         | 
| 57 | 
            +
                    this.pollPeriodically(path, this.setStateFromJSON, this.props.showErrorModal);
         | 
| 58 | 
            +
                }
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                pollPeriodically(path, callback, errCallback) {
         | 
| 55 61 | 
             
                    var intervals = [200, 400, 800, 1200, 2000, 3000, 5000];
         | 
| 56 | 
            -
                    var component = this;
         | 
| 57 62 | 
             
                    function poll() {
         | 
| 58 | 
            -
                        $.getJSON( | 
| 63 | 
            +
                        $.getJSON(path).complete(function (jqXHR) {
         | 
| 59 64 | 
             
                            switch (jqXHR.status) {
         | 
| 60 65 | 
             
                            case 202:
         | 
| 61 66 | 
             
                                var interval;
         | 
| @@ -67,12 +72,12 @@ class Report extends Component { | |
| 67 72 | 
             
                                setTimeout(poll, interval);
         | 
| 68 73 | 
             
                                break;
         | 
| 69 74 | 
             
                            case 200:
         | 
| 70 | 
            -
                                 | 
| 75 | 
            +
                                callback(jqXHR.responseJSON);
         | 
| 71 76 | 
             
                                break;
         | 
| 72 | 
            -
                            case 404:
         | 
| 73 77 | 
             
                            case 400:
         | 
| 78 | 
            +
                            case 422:
         | 
| 74 79 | 
             
                            case 500:
         | 
| 75 | 
            -
                                 | 
| 80 | 
            +
                                errCallback(jqXHR.responseJSON);
         | 
| 76 81 | 
             
                                break;
         | 
| 77 82 | 
             
                            }
         | 
| 78 83 | 
             
                        });
         | 
| @@ -93,6 +98,7 @@ class Report extends Component { | |
| 93 98 | 
             
                        this.setState(responseJSON, this.prepareAlignmentOfAllHits);
         | 
| 94 99 | 
             
                    }
         | 
| 95 100 | 
             
                }
         | 
| 101 | 
            +
             | 
| 96 102 | 
             
                /**
         | 
| 97 103 | 
             
               * Called as soon as the page has loaded and the user sees the loading spinner.
         | 
| 98 104 | 
             
               * We use this opportunity to setup services that make use of delegated events
         | 
| @@ -100,6 +106,7 @@ class Report extends Component { | |
| 100 106 | 
             
               */
         | 
| 101 107 | 
             
                componentDidMount() {
         | 
| 102 108 | 
             
                    this.fetchResults();
         | 
| 109 | 
            +
                    this.plugins.init();
         | 
| 103 110 | 
             
                    // This sets up an event handler which enables users to select text from
         | 
| 104 111 | 
             
                    // hit header without collapsing the hit.
         | 
| 105 112 | 
             
                    this.preventCollapseOnSelection();
         | 
| @@ -112,7 +119,7 @@ class Report extends Component { | |
| 112 119 | 
             
               * and circos would have been rendered at this point. At this stage we kick
         | 
| 113 120 | 
             
               * start iteratively adding 1 HSP to the page every 25 milli-seconds.
         | 
| 114 121 | 
             
               */
         | 
| 115 | 
            -
                componentDidUpdate() {
         | 
| 122 | 
            +
                componentDidUpdate(prevProps, prevState) {
         | 
| 116 123 | 
             
                    // Log to console how long the last update take?
         | 
| 117 124 | 
             
                    // console.log((Date.now() - this.lastTimeStamp) / 1000);
         | 
| 118 125 |  | 
| @@ -130,6 +137,8 @@ class Report extends Component { | |
| 130 137 | 
             
                    } else {
         | 
| 131 138 | 
             
                        this.componentFinishedUpdating();
         | 
| 132 139 | 
             
                    }
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                    this.plugins.componentDidUpdate(prevProps, prevState);
         | 
| 133 142 | 
             
                }
         | 
| 134 143 |  | 
| 135 144 | 
             
                /**
         | 
| @@ -140,13 +149,14 @@ class Report extends Component { | |
| 140 149 | 
             
                    var numHSPsProcessed = 0;
         | 
| 141 150 | 
             
                    while (this.nextQuery < this.state.queries.length) {
         | 
| 142 151 | 
             
                        var query = this.state.queries[this.nextQuery];
         | 
| 152 | 
            +
             | 
| 143 153 | 
             
                        // We may see a query multiple times during rendering because only
         | 
| 144 | 
            -
                        // 3 hsps  | 
| 154 | 
            +
                        // 3 hsps are rendered in each cycle, but we want to create the
         | 
| 145 155 | 
             
                        // corresponding Query component only the first time we see it.
         | 
| 146 156 | 
             
                        if (this.nextHit == 0 && this.nextHSP == 0) {
         | 
| 147 157 | 
             
                            results.push(
         | 
| 148 158 | 
             
                                <ReportQuery
         | 
| 149 | 
            -
                                    key={'Query_' + query. | 
| 159 | 
            +
                                    key={'Query_' + query.id}
         | 
| 150 160 | 
             
                                    query={query}
         | 
| 151 161 | 
             
                                    program={this.state.program}
         | 
| 152 162 | 
             
                                    querydb={this.state.querydb}
         | 
| @@ -156,6 +166,8 @@ class Report extends Component { | |
| 156 166 | 
             
                                    veryBig={this.state.veryBig}
         | 
| 157 167 | 
             
                                />
         | 
| 158 168 | 
             
                            );
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                            results.push(...this.plugins.queryResults(query));
         | 
| 159 171 | 
             
                        }
         | 
| 160 172 |  | 
| 161 173 | 
             
                        while (this.nextHit < query.hits.length) {
         | 
| @@ -190,11 +202,11 @@ class Report extends Component { | |
| 190 202 | 
             
                                    <HSP
         | 
| 191 203 | 
             
                                        key={
         | 
| 192 204 | 
             
                                            'Query_' +
         | 
| 193 | 
            -
             | 
| 194 | 
            -
             | 
| 195 | 
            -
             | 
| 196 | 
            -
             | 
| 197 | 
            -
             | 
| 205 | 
            +
                                            query.number +
         | 
| 206 | 
            +
                                            '_Hit_' +
         | 
| 207 | 
            +
                                            hit.number +
         | 
| 208 | 
            +
                                            '_HSP_' +
         | 
| 209 | 
            +
                                            hsp.number
         | 
| 198 210 | 
             
                                        }
         | 
| 199 211 | 
             
                                        query={query}
         | 
| 200 212 | 
             
                                        hit={hit}
         | 
| @@ -261,6 +273,9 @@ class Report extends Component { | |
| 261 273 | 
             
                                    <br />
         | 
| 262 274 | 
             
                        You can bookmark the page and come back to it later or share the
         | 
| 263 275 | 
             
                        link with someone.
         | 
| 276 | 
            +
                                    <br />
         | 
| 277 | 
            +
                                    <br />
         | 
| 278 | 
            +
                                    { process.env.targetEnv === 'cloud' && <b>If the job takes more than 10 minutes to complete, we will send you an email upon completion.</b> }
         | 
| 264 279 | 
             
                                </p>
         | 
| 265 280 | 
             
                            </div>
         | 
| 266 281 | 
             
                        </div>
         | 
| @@ -451,7 +466,7 @@ class Report extends Component { | |
| 451 466 | 
             
                toggleTable() {
         | 
| 452 467 | 
             
                    $('body').on(
         | 
| 453 468 | 
             
                        'mousedown',
         | 
| 454 | 
            -
                        '.resultn  | 
| 469 | 
            +
                        '.resultn .caption[data-toggle="collapse"]',
         | 
| 455 470 | 
             
                        function (event) {
         | 
| 456 471 | 
             
                            var $this = $(this);
         | 
| 457 472 | 
             
                            $this.on('mouseup mousemove', function handler(event) {
         | 
| @@ -509,6 +524,7 @@ class Report extends Component { | |
| 509 524 | 
             
                    } else {
         | 
| 510 525 | 
             
                        $hit.removeClass('glow');
         | 
| 511 526 | 
             
                        $hit.next('.hsp').removeClass('glow');
         | 
| 527 | 
            +
                        $('.download-fasta-of-selected').attr('href', '#').removeAttr('download');
         | 
| 512 528 | 
             
                    }
         | 
| 513 529 |  | 
| 514 530 | 
             
                    var $a = $('.download-fasta-of-selected');
         | 
    
        data/public/js/search.js
    CHANGED
    
    | @@ -3,14 +3,6 @@ import React, { Component } from "react"; | |
| 3 3 | 
             
            import { createRoot } from "react-dom/client";
         | 
| 4 4 | 
             
            import { DnD } from "./dnd";
         | 
| 5 5 | 
             
            import { Form } from "./form";
         | 
| 6 | 
            -
            /**
         | 
| 7 | 
            -
             * Load necessary polyfills.
         | 
| 8 | 
            -
             */
         | 
| 9 | 
            -
            $.webshims.setOptions(
         | 
| 10 | 
            -
              "basePath",
         | 
| 11 | 
            -
              "/vendor/npm/webshim@1.15.8/js-webshim/minified/shims/"
         | 
| 12 | 
            -
            );
         | 
| 13 | 
            -
            $.webshims.polyfill("forms");
         | 
| 14 6 |  | 
| 15 7 | 
             
            /**
         | 
| 16 8 | 
             
             * Clear sessionStorage on reload.
         | 
    
        data/public/js/sidebar.js
    CHANGED
    
    | @@ -4,7 +4,7 @@ import _ from 'underscore'; | |
| 4 4 | 
             
            import downloadFASTA from './download_fasta';
         | 
| 5 5 | 
             
            import asMailtoHref from './mailto';
         | 
| 6 6 | 
             
            import CloudShareModal from './cloud_share_modal';
         | 
| 7 | 
            -
             | 
| 7 | 
            +
            import DownloadLinks from 'download_links';
         | 
| 8 8 | 
             
            /**
         | 
| 9 9 | 
             
             * checks whether code is being run by jest
         | 
| 10 10 | 
             
             */
         | 
| @@ -172,6 +172,9 @@ export default class extends Component { | |
| 172 172 | 
             
                    var sequence_ids = $('.hit-links :checkbox:checked').map(function () {
         | 
| 173 173 | 
             
                        return this.value;
         | 
| 174 174 | 
             
                    }).get();
         | 
| 175 | 
            +
                    if (sequence_ids.length === 0) {
         | 
| 176 | 
            +
                        return false;
         | 
| 177 | 
            +
                    }
         | 
| 175 178 | 
             
                    var database_ids = _.map(this.props.data.querydb, _.iteratee('id'));
         | 
| 176 179 | 
             
                    downloadFASTA(sequence_ids, database_ids);
         | 
| 177 180 | 
             
                    return false;
         | 
| @@ -347,6 +350,7 @@ export default class extends Component { | |
| 347 350 | 
             
                                        </a>
         | 
| 348 351 | 
             
                                    </li>
         | 
| 349 352 | 
             
                                }
         | 
| 353 | 
            +
                                <DownloadLinks imported_xml={this.props.data.imported_xml} search_id={this.props.data.search_id} />
         | 
| 350 354 | 
             
                            </ul>
         | 
| 351 355 | 
             
                        </div>
         | 
| 352 356 | 
             
                    );
         | 
| @@ -406,6 +410,12 @@ export default class extends Component { | |
| 406 410 | 
             
                            {this.topPanelJSX()}
         | 
| 407 411 | 
             
                            {this.downloadsPanelJSX()}
         | 
| 408 412 | 
             
                            {this.sharingPanelJSX()}
         | 
| 413 | 
            +
                            <div className="referral-panel">
         | 
| 414 | 
            +
                                <div className="section-header-sidebar">
         | 
| 415 | 
            +
                                    <h4>Recommend SequenceServer</h4>
         | 
| 416 | 
            +
                                    <p><a href="https://sequenceserver.com/referral-program" target="_blank">Earn up to $100 per signup</a></p>
         | 
| 417 | 
            +
                                </div>
         | 
| 418 | 
            +
                            </div>
         | 
| 409 419 | 
             
                        </div>
         | 
| 410 420 | 
             
                    );
         | 
| 411 421 | 
             
                }
         | 
| @@ -29,4 +29,21 @@ GAGATGGAAATGGCCGATTACCCGCTCGCCTATGATATTTCCCCGTATCTTCCGCCGTTC | |
| 29 29 | 
             
            CTGTCGCGAGCGAGGGCACGGGGAATGTTAGACGGTCGCTTCGCCGGCAGACGCTACCGA
         | 
| 30 30 | 
             
            AGGGAGTCGCGGGGCATTCACGAGGAGTGTTGCATCAACGGATGTACGATAAACGAATTG
         | 
| 31 31 | 
             
            ACCAGCTACTGCGGCCCC
         | 
| 32 | 
            -
            `;
         | 
| 32 | 
            +
            `;
         | 
| 33 | 
            +
             | 
| 34 | 
            +
            export const FASTQ_SEQUENCE =
         | 
| 35 | 
            +
            `@SRR001666.1 071112_SLXA-EAS1_s_7:5:1:817:345 length=72
         | 
| 36 | 
            +
            GGGTGATGGCCGCTGCCGATGGCGTCAAATCCCACCAAGTTACCCTTAACAACTTAAGGGTTTTCAAATAGA
         | 
| 37 | 
            +
            +SRR001666.1 071112_SLXA-EAS1_s_7:5:1:817:345 length=72
         | 
| 38 | 
            +
            IIIIIIIIIIIIIIIIIIIIIIIIIIIIII9IG9ICIIIIIIIIIIIIIIIIIIIIDIIIIIII>IIIIII/
         | 
| 39 | 
            +
            @SRR001666.2 071112_SLXA-EAS1_s_7:5:1:801:338 length=72
         | 
| 40 | 
            +
            GTTCAGGGATACGACGTTTGTATTTTAAGAATCTGAAGCAGAAGTCGATGATAATACGCGTCGTTTTATCAT
         | 
| 41 | 
            +
            +SRR001666.2 071112_SLXA-EAS1_s_7:5:1:801:338 length=72
         | 
| 42 | 
            +
            IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII6IBIIIIIIIIIIIIIIIIIIIIIIIGII>IIIII-I)8I
         | 
| 43 | 
            +
            `;
         | 
| 44 | 
            +
             | 
| 45 | 
            +
            export const FASTA_OF_FASTQ_SEQUENCE =
         | 
| 46 | 
            +
            `>SRR001666.1 071112_SLXA-EAS1_s_7:5:1:817:345 length=72
         | 
| 47 | 
            +
            GGGTGATGGCCGCTGCCGATGGCGTCAAATCCCACCAAGTTACCCTTAACAACTTAAGGGTTTTCAAATAGA
         | 
| 48 | 
            +
            >SRR001666.2 071112_SLXA-EAS1_s_7:5:1:801:338 length=72
         | 
| 49 | 
            +
            GTTCAGGGATACGACGTTTGTATTTTAAGAATCTGAAGCAGAAGTCGATGATAATACGCGTCGTTTTATCAT`;
         | 
| @@ -3,7 +3,7 @@ | |
| 3 3 | 
             
            import { render, screen, fireEvent } from '@testing-library/react';
         | 
| 4 4 | 
             
            import { SearchQueryWidget } from '../query';
         | 
| 5 5 | 
             
            import { Form } from '../form';
         | 
| 6 | 
            -
            import { AMINO_ACID_SEQUENCE, NUCLEOTIDE_SEQUENCE } from './mock_data/sequences';
         | 
| 6 | 
            +
            import { AMINO_ACID_SEQUENCE, NUCLEOTIDE_SEQUENCE, FASTQ_SEQUENCE, FASTA_OF_FASTQ_SEQUENCE } from './mock_data/sequences';
         | 
| 7 7 | 
             
            import '@testing-library/jest-dom/extend-expect';
         | 
| 8 8 | 
             
            import '@testing-library/react/dont-cleanup-after-each';
         | 
| 9 9 |  | 
| @@ -16,7 +16,7 @@ describe('SEARCH COMPONENT', () => { | |
| 16 16 | 
             
                    } />).container;
         | 
| 17 17 | 
             
                    inputEl = screen.getByRole('textbox', { name: '' });
         | 
| 18 18 | 
             
                });
         | 
| 19 | 
            -
             | 
| 19 | 
            +
             | 
| 20 20 | 
             
                test('should render the search component textarea', () => {
         | 
| 21 21 | 
             
                    expect(inputEl).toHaveClass('form-control');
         | 
| 22 22 | 
             
                });
         | 
| @@ -47,7 +47,7 @@ describe('SEARCH COMPONENT', () => { | |
| 47 47 | 
             
                    expect(activeNotification.id).toBe('nucleotide-sequence-notification');
         | 
| 48 48 | 
             
                    expect(alertWrapper).toHaveTextContent('Detected: nucleotide sequence(s).');
         | 
| 49 49 | 
             
                });
         | 
| 50 | 
            -
             | 
| 50 | 
            +
             | 
| 51 51 | 
             
                test('should correctly detect the mixed sequences and show error notification', () => {
         | 
| 52 52 | 
             
                    fireEvent.change(inputEl, { target: { value: `${NUCLEOTIDE_SEQUENCE}${AMINO_ACID_SEQUENCE}` } });
         | 
| 53 53 | 
             
                    const activeNotification = container.querySelector('.notification.active');
         | 
| @@ -55,4 +55,13 @@ describe('SEARCH COMPONENT', () => { | |
| 55 55 | 
             
                    const alertWrapper = activeNotification.children[0];
         | 
| 56 56 | 
             
                    expect(alertWrapper).toHaveTextContent('Error: mixed nucleotide and amino-acid sequences detected.');
         | 
| 57 57 | 
             
                });
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                test('should correctly detect FASTQ and convert it to FASTA', () => {
         | 
| 60 | 
            +
                    fireEvent.change(inputEl, { target: { value: FASTQ_SEQUENCE } });
         | 
| 61 | 
            +
                    const activeNotification = container.querySelector('.notification.active');
         | 
| 62 | 
            +
                    const alertWrapper = activeNotification.children[0];
         | 
| 63 | 
            +
                    expect(activeNotification.id).toBe('fastq-sequence-notification');
         | 
| 64 | 
            +
                    expect(alertWrapper).toHaveTextContent('Detected FASTQ and automatically converted to FASTA.');
         | 
| 65 | 
            +
                    expect(inputEl).toHaveValue(FASTA_OF_FASTQ_SEQUENCE);
         | 
| 66 | 
            +
                });
         | 
| 58 67 | 
             
            });
         |