diff --git a/browser/js/aligner.js b/browser/js/aligner.js index 4e960faa18a4abe56f7caeab481c879a1f67895a..24627c424127645ff25c9c898c4d739ad763e49a 100644 --- a/browser/js/aligner.js +++ b/browser/js/aligner.js @@ -279,7 +279,7 @@ Aligner.prototype = { connect_checkbox: function(elem, title, layers){ var self = this; - + //connect event to checkbox and checkbox_label $(function () { $(elem).find('input').unbind("click"); @@ -289,6 +289,15 @@ Aligner.prototype = { localStorage.setItem(`aligner_layers_${title}`, input.checked) } self.toggleLayers(layers, input.checked, true); + + var exclusive = ["amino", "affect_values"] + exclusive.forEach( (layer, index) => { + var to_exclude = index === 0 ? exclusive[1]: exclusive[0] + if (input.checked && layers[0] == layer){ + var checkbox = document.getElementById(`aligner_checkbox_${to_exclude}`) + if (checkbox.checked) {checkbox.click()} // disable affect; click on corresponding button + } + }) e.stopPropagation(); }); }); diff --git a/browser/js/aligner_layer.js b/browser/js/aligner_layer.js index 84382e792c21c6b73446827fa5f333ee9f9e7030..79b28054842bbdff80efb349917c44169877071a 100644 --- a/browser/js/aligner_layer.js +++ b/browser/js/aligner_layer.js @@ -4,7 +4,7 @@ LAYERS = { 'nuc': { 'className': "seq_layer_text", - 'condition': function (s,c) { return !LAYERS.amino.enabled; }, + 'condition': function (s,c) { return !LAYERS.amino.enabled && !LAYERS.affect_values.enabled; }, 'text': function (s,c) { return s.nucleoString(); }, 'enabled': true }, @@ -12,10 +12,20 @@ LAYERS = { 'amino': { 'className': "seq_layer_text", + 'condition': function (s,c) { return !LAYERS.affect_values.enabled; }, 'text': function (s,c) { return s.aminoString(); }, 'enabled': false }, + + 'affect_values': + { + 'className': "seq_layer_text", + 'condition': function (s,c) { return !LAYERS.amino.enabled; }, + 'text': function (s,c) { return s.affectValuesString(); }, + 'enabled': false + }, + 'amino_separator': { 'className': "seq_layer_text", diff --git a/browser/js/aligner_menu.js b/browser/js/aligner_menu.js index 8b2c28a9e211b1d28f1c814016ef43a3fc991e58..68d306ffca1a3152112efd027335a660db302149 100644 --- a/browser/js/aligner_menu.js +++ b/browser/js/aligner_menu.js @@ -75,6 +75,12 @@ ALIGNER_MENU = { 'layers': ["amino"], 'title': 'Show Amino Acid sequences (AA positions based on CDR3)', 'enabled': false + },{ + 'id': 'affect_values', + 'text': 'Use affect sequence', + 'layers': ["affect_values"], + 'title': 'Show affectation values sequences (Kmers that match against germline sequences)', + 'enabled': false }] } }; \ No newline at end of file diff --git a/browser/js/aligner_sequence.js b/browser/js/aligner_sequence.js index 4ee32eaced6a622bb225795155e899c80912c4e9..e4671dc1bcb1b11cd1183f0639393d56af5d64f0 100644 --- a/browser/js/aligner_sequence.js +++ b/browser/js/aligner_sequence.js @@ -184,6 +184,21 @@ Sequence.prototype = { } }, + /** + * Return the assign string of clonotype + * */ + affectValuesString: function () { + if (isNaN(this.id)) return "" + + var clone = this.m.clone(this.id); + if (!clone.hasSequence()) return ""; + if (clone.seg.affectValues != undefined && clone.seg.affectValues.seq != undefined){ + return clone.seg.affectValues.seq + } + return "" + + }, + aminoSplitString: function () { var start = -1; var stop = -1; @@ -381,6 +396,8 @@ Sequence.prototype = { seq = this.align(this.seqAA.join(''), SYMBOL_VOID) var r = this.segmenter.sequence[this.segmenter.sequence_order[0]]; ref = r.aminoString(); + } else if (LAYERS.affect_values.enabled) { + return this.affectValuesString(); } else { seq = this.seq; ref = this.segmenter.sequence[this.segmenter.sequence_order[0]].seq; diff --git a/browser/test/QUnit/testFiles/data_test.js b/browser/test/QUnit/testFiles/data_test.js index e11c346837576cf025c0654c228919f3dc0c5ee6..159483fe9ceacceb2b38fd38be7cb72b8eb28b50 100644 --- a/browser/test/QUnit/testFiles/data_test.js +++ b/browser/test/QUnit/testFiles/data_test.js @@ -300,6 +300,11 @@ json_data.clones = [ "5end": 178, "5start": 0, "N": 4, + "affectValues": { + "seq": "GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG_____gggggggggggggggggggggggggggggggggggggggggggggggggggggggggg", + "start": 1, + "stop": 324 + }, "imgt": { "Sequence ID": "TRGV3*01_-0/4/-5_TRGJP2*01", "Functionality": "productive", diff --git a/browser/test/QUnit/testFiles/segmenter_test.js b/browser/test/QUnit/testFiles/segmenter_test.js index f3bda88302e91c49f8092abb32c073f51fb94c59..e178354aa37e901e6e4115045c9fd7f91fda3449 100644 --- a/browser/test/QUnit/testFiles/segmenter_test.js +++ b/browser/test/QUnit/testFiles/segmenter_test.js @@ -76,7 +76,7 @@ QUnit.test("segmenter", function(assert) { }, delay+=step); setTimeout( function() { - assert.deepEqual(segment.findPotentialField(), ["","cdr3","fr1", "5", "f1", "V-REGION","J-REGION","D-REGION","CDR3-IMGT"], "potentialField : Ok") + assert.deepEqual(segment.findPotentialField(), ["","cdr3","fr1", "5", "affectValues", "f1", "V-REGION","J-REGION","D-REGION","CDR3-IMGT"], "potentialField : Ok") m.select(0) m.focusIn(0) @@ -185,6 +185,12 @@ QUnit.test("sequence", function(assert) { var amino4 = seq4.aminoString().replace(/(?=\s)[^\r\n\t]/g, '_'); assert.equal(amino4, "__I_##_H__D__A__T__I__L__?", amino4 + " amino sequence"); + var affect3 = seq3.affectValuesString() + assert.equal(affect3, "GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG_____gggggggggggggggggggggggggggggggggggggggggggggggggggggggggg", "coorect affect sequence"); + + var affect4 = seq4.affectValuesString() + assert.equal(affect4, "", "affect sequence (not present in clone data)"); + var aminoSplit4 = seq4.aminoSplitString().replace(/(?=\s)[^\r\n\t]/g, '_'); assert.equal(aminoSplit4, "___|_|__|__|__|__|__|__|_|", aminoSplit4 + " aminoSplit sequence "); diff --git a/browser/test/cypress/e2e/external_aligner.cy.js b/browser/test/cypress/e2e/external_aligner.cy.js index df933f2bba7aa64583421c11af84314202ddfb2b..b53e161772ec62cc4b8d2e9c528b7f4dd8cff7a7 100644 --- a/browser/test/cypress/e2e/external_aligner.cy.js +++ b/browser/test/cypress/e2e/external_aligner.cy.js @@ -21,7 +21,7 @@ describe('External Aligner', function () { cy.get('#segmenter_axis_select').children().should('have.length', 10) cy.get('#align-settings').click({force:true}) - cy.get('#align-settings_select').children().should('have.length', 4) + cy.get('#align-settings_select').children().should('have.length', 5) cy.get('#align-segment-info').click({force:true}) cy.get('#align-segment-info_select').children().should('have.length', 7) diff --git a/browser/test/cypress/e2e/test_aligner.cy.js b/browser/test/cypress/e2e/test_aligner.cy.js index 859b7b6e7d6608f15272512c128c54ce73908ef2..2b2483027b862c07927ac6f4359839a4c3911fe6 100644 --- a/browser/test/cypress/e2e/test_aligner.cy.js +++ b/browser/test/cypress/e2e/test_aligner.cy.js @@ -20,7 +20,7 @@ describe('Aligner', function () { cy.get('#segmenter_axis_select').children().should('have.length', 10) cy.get('#align-settings').click({force:true}) - cy.get('#align-settings_select').children().should('have.length', 4) + cy.get('#align-settings_select').children().should('have.length', 5) cy.get('#align-segment-info').click({force:true}) cy.get('#align-segment-info_select').children().should('have.length', 7) @@ -219,6 +219,57 @@ describe('Aligner', function () { return }) + + + + it('Aligner layers, see affect and amino (and exclusive)', function() { + cy.openAnalysis("browser/test/data/demo_lil_l3_one_full_clone.vidjil") + cy.selectClone(0) + cy.get('#seq0 > .sequence-holder > .seq-fixed > .axisBox > .Size > .sizeBox') + .should("exist") + .should("be.visible") + + // Initial view + cy.get('.seq_layer_nuc') + .should("contain", "GGGGGAGGCTTGGTACAGCCTGGCAGGTCCCTGAGACTCTCCTGTGCAGCCTCTGGATTCACCTTTGATGATTATGCCATGCACTGGGTCCGGCAAGCTCCAGGGAAGGGCCTGGAGTGGGTCTCAGGTATTAGTTGGAATAGTGGTAGCATAGGCTATGCGGACTCTGTGAAGGGCCGATTCACCATCTCCAGAGACAACGCCAAGAACTCCCTGTATCTGCAAATGAACAGTCTGAGAGCTGAGGACACGGCCTTGTATTACTGTGCACCCGGAGGTATGGACGTCTGGGGCCAAGGGACCCTGGTCACCGTCTCCTCAGGT") + + // First check affect + cy.get("#aligner_checkbox_affect_values") + .check({force: true}) + cy.get('.seq_layer_affect_values') + .should("contain", "HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH?HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH?HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH?HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH?HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH_________________hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh_____________") + + cy.get("#aligner_checkbox_amino") + .should('not.be.checked'); + + // Check amino + cy.get("#aligner_checkbox_amino") + .check({force: true}) + + cy.get('.seq_layer_amino') + .contains("G") // exclusive letter neither in nucleotide or affect + cy.get("#aligner_checkbox_affect_values") + .should('not.be.checked'); + + // Check again affect + cy.get("#aligner_checkbox_affect_values") + .check({force: true}) + cy.get('.seq_layer_affect_values') + .should("contain", "HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH?HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH?HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH?HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH?HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH_________________hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh_____________") + cy.get("#aligner_checkbox_amino") + .should('not.be.checked'); + + + // Uncheck affect, should return to raw sequence + cy.get("#aligner_checkbox_affect_values") + .check({force: true}) + + cy.get('.seq_layer_nuc') + .should("contain", "GGGGGAGGCTTGGTACAGCCTGGCAGGTCCCTGAGACTCTCCTGTGCAGCCTCTGGATTCACCTTTGATGATTATGCCATGCACTGGGTCCGGCAAGCTCCAGGGAAGGGCCTGGAGTGGGTCTCAGGTATTAGTTGGAATAGTGGTAGCATAGGCTATGCGGACTCTGTGAAGGGCCGATTCACCATCTCCAGAGACAACGCCAAGAACTCCCTGTATCTGCAAATGAACAGTCTGAGAGCTGAGGACACGGCCTTGTATTACTGTGCACCCGGAGGTATGGACGTCTGGGGCCAAGGGACCCTGGTCACCGTCTCCTCAGGT") + + }) + + //menu in aligner top right corner (focus/hide/tag) are already tested in test_filter.js diff --git a/browser/test/data/demo_lil_l3_one_full_clone.vidjil b/browser/test/data/demo_lil_l3_one_full_clone.vidjil new file mode 100644 index 0000000000000000000000000000000000000000..8b97f8b601503d84ad29b2d17194dd00f04d7688 --- /dev/null +++ b/browser/test/data/demo_lil_l3_one_full_clone.vidjil @@ -0,0 +1,191 @@ +{ + "clones": [ + { + "_average_read_length": [ + "317.35" + ], + "_coverage": [ + 1.0209637880325317 + ], + "_coverage_info": [ + "324 bp (102% of 317.3 bp)" + ], + "germline": "IGH", + "id": "CACGGCCTTGTATTACTGTGCACCCGGAGGTATGGACGTCTGGGGCCAAG", + "name": "IGHV3-9*01 7/CCCGGA/17 IGHJ6*02", + "reads": [ + 189991 + ], + "seg": { + "3": { + "delLeft": 17, + "delRight": 60, + "name": "IGHJ6*02", + "start": 277, + "stop": 321 + }, + "5": { + "delRight": 7, + "name": "IGHV3-9*01", + "stop": 270 + }, + "N": 6, + "affectSigns": { + "seq": "+++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++ ", + "start": 1, + "stop": 324 + }, + "affectValues": { + "seq": "HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH?HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH?HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH?HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH?HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH_________________hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh_____________", + "start": 1, + "stop": 324 + }, + "cdr3": { + "aa": "APGGMDV", + "seq": "GCACCCGGAGGTATGGACGTC", + "start": 268, + "stop": 288 + }, + "evalue": { + "val": "3.07e-136" + }, + "evalue_left": { + "val": "0.00e+00" + }, + "evalue_right": { + "val": "3.07e-136" + }, + "junction": { + "aa": "CAPGGMDVW", + "productive": true, + "start": 265, + "stop": 291 + }, + "quality": { + "seq": "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!=>>?8?8>7=??@8>>=>==>=>?2<6;?9>=>=9?>:;;==>>.<4:4:!!!!!!!!!!!!!!!!!!!!!!!!!!", + "start": 1, + "stop": 324 + } + }, + "sequence": "GGGGGAGGCTTGGTACAGCCTGGCAGGTCCCTGAGACTCTCCTGTGCAGCCTCTGGATTCACCTTTGATGATTATGCCATGCACTGGGTCCGGCAAGCTCCAGGGAAGGGCCTGGAGTGGGTCTCAGGTATTAGTTGGAATAGTGGTAGCATAGGCTATGCGGACTCTGTGAAGGGCCGATTCACCATCTCCAGAGACAACGCCAAGAACTCCCTGTATCTGCAAATGAACAGTCTGAGAGCTGAGGACACGGCCTTGTATTACTGTGCACCCGGAGGTATGGACGTCTGGGGCCAAGGGACCCTGGTCACCGTCTCCTCAGGT", + "top": 1 + } + ], + "config": { + "all": false, + "alternative-genes": "0", + "analysis-e-value-D": "0.05", + "analysis-filter": "1", + "clean-memory": false, + "cluster-N": "10", + "cluster-epsilon": "0", + "cluster-load-matrix": false, + "cluster-save-matrix": false, + "consensus-on-longest-sequences": false, + "dir": "lil-l3-0-clone0", + "e-value": "1", + "e-value-kmer": "-1", + "filter-reads": false, + "germline": "germline/homo-sapiens.g", + "gz": false, + "help-advanced": false, + "label-filter": false, + "max-consensus": "100", + "min-ratio": "0", + "min-reads": "5", + "no-airr": false, + "no-vidjil": false, + "not-analyzed-as-clones": false, + "out-affects": false, + "out-clone-files": false, + "out-details": false, + "out-detected": false, + "out-reads": false, + "out-undetected": false, + "out-vdjfa": false, + "plain-index": false, + "several-D": false, + "show-junction": false, + "verbose": false + }, + "diversity": { + "index_Ds_diversity": 0.0, + "index_E_equitability": 0.0, + "index_H_entropy": 0.0 + }, + "germlines": { + "ref": "https://www.vidjil.org/germlines/germline-2021-01-21.tar.gz", + "species": "Homo sapiens", + "species_taxon_id": 9606 + }, + "reads": { + "germline": { + "IGH": [ + 189991 + ], + "IGH+": [ + 0 + ], + "IGK": [ + 0 + ], + "IGK+": [ + 0 + ], + "IGL": [ + 0 + ], + "TRA": [ + 0 + ], + "TRA+D": [ + 0 + ], + "TRB": [ + 0 + ], + "TRB+": [ + 0 + ], + "TRD": [ + 0 + ], + "TRD+": [ + 0 + ], + "TRG": [ + 0 + ] + }, + "segmented": [ + 189991 + ], + "total": [ + 189995 + ] + }, + "samples": { + "commandline": [ + "vidjil-algo -g germline/homo-sapiens.g -o lil-l3-0-clone0 full_reads_forward_reverse.fastq " + ], + "log": [ + " ==> junction detected in 189991 reads (100%)\n ==> found 1 windows in 189991 reads (100% of 189995 reads)\n reads av. len clones clo/rds\n IGH -> 189991 317.3 1 0.000\n IGH+ -> 0 - 0 -\n IGK -> 0 - 0 -\n IGK+ -> 0 - 0 -\n IGL -> 0 - 0 -\n TRA -> 0 - 0 -\n TRA+D -> 0 - 0 -\n TRB -> 0 - 0 -\n TRB+ -> 0 - 0 -\n TRD -> 0 - 0 -\n TRD+ -> 0 - 0 -\n TRG -> 0 - 0 -\n\n SEG -> 189991 317.3\n SEG_+ -> 55411 340.2\n SEG_- -> 134580 308.0\n SEG changed w -> 0 -\n\n UNSEG too short -> 0 -\n UNSEG strand -> 4 208.8\n UNSEG too few V/J -> 0 -\n UNSEG only V/5' -> 0 -\n UNSEG only J/3' -> 0 -\n UNSEG < delta_min -> 0 -\n UNSEG ambiguous -> 0 -\n UNSEG too short w -> 0 -\n" + ], + "number": 1, + "original_names": [ + "full_reads_forward_reverse.fastq" + ], + "producer": [ + "vidjil-algo dev b59846a2a (2022-01-11)" + ], + "run_timestamp": [ + "2024-09-06 14:32:25" + ] + }, + "similarity": [ + [ + 0.0 + ] + ], + "vidjil_json_version": "2016b" +} \ No newline at end of file