From 214033f6c24dee1c75ee5bdb53d28f18cc2dc67d Mon Sep 17 00:00:00 2001 From: mimadrid Date: Mon, 24 Jul 2017 16:16:14 +0200 Subject: [PATCH] Changes: - Rewrite NGL wrapper - Add interaction with the sequences viewer - Split features by type - Redesing a bit the UI --- www/views/public/js/ngl.js | 277 ++++---- www/views/public/js/protein_tool.js | 42 -- www/views/public/js/rbbt.protein_tool.js | 80 +-- www/views/tools/protein_tool.haml | 79 ++- .../protein_tool/Mutations&Features_tab.haml | 19 + .../tools/protein_tool/PDB-Viewer_tab.haml | 602 ++++++++++-------- .../mutations&features_tabs/Appris_tab.haml | 33 + .../COSMIC_tab.haml | 32 +- .../mutations&features_tabs/UniProt_tab.haml | 33 + 9 files changed, 684 insertions(+), 513 deletions(-) delete mode 100644 www/views/public/js/protein_tool.js create mode 100644 www/views/tools/protein_tool/Mutations&Features_tab.haml create mode 100644 www/views/tools/protein_tool/mutations&features_tabs/Appris_tab.haml rename www/views/tools/protein_tool/{ => mutations&features_tabs}/COSMIC_tab.haml (64%) create mode 100644 www/views/tools/protein_tool/mutations&features_tabs/UniProt_tab.haml diff --git a/www/views/public/js/ngl.js b/www/views/public/js/ngl.js index ea9a332..e424314 100644 --- a/www/views/public/js/ngl.js +++ b/www/views/public/js/ngl.js @@ -1,17 +1,20 @@ -require_js(["/js-find/ngl/ngl.min.js"], function() {y: - $.widget('rbbt.ngl_tool', { +/** + * Viewer: a NGL wrapper + */ +require_js(["/js-find/ngl/ngl.min.js"], function() { + $.widget('rbbt.ngl_viewer', { options: { pdb_url: null, pdb_url_download: null, - pdb: url, + pdb: null, component_promise: null, stage: null, - colorScheme: null, + colorScheme: [], seq2pdb: null, pdb2seq: null, - appris_features: null, + default_colors: null, marks: {}, - default_colors: {'mark': 'red', 'original_alignment': 'darkslategray', 'align': 'blue', 'feature': 'purple', 'default': 'lightgray'} + layers_sequence: [] }, _create: function() { @@ -28,10 +31,10 @@ require_js(["/js-find/ngl/ngl.min.js"], function() {y: fontFamily: "sans-serif" }); - this._init() + this._init(); // For use this inside mouse hooks - var ths = this + var ths = this; this.options.stage.viewer.container.appendChild(tooltip); // remove default hoverPick mouse action @@ -42,17 +45,17 @@ require_js(["/js-find/ngl/ngl.min.js"], function() {y: if(pickingProxy && (pickingProxy.atom || pickingProxy.bond)){ var atom = pickingProxy.atom || pickingProxy.closestBondAtom; var cp = pickingProxy.canvasPosition; - group = atom.qualifiedName().match(/\[(\w+)\](\d+)(.+)/) - aa = group[1] - pos_pdb = group[2] - first_pos_chain = Object.keys(ths.options.pdb2seq)[0] - chain_letters = new Set(Object.keys(ths.options.pdb2seq).map((pdb_pos) => pdb_pos[0])) + var group = atom.qualifiedName().match(/\[(\w+)\](\d+)(.+)/); + var aa = group[1]; + var pos_pdb = group[2]; + var first_pos_chain = Object.keys(ths.options.pdb2seq)[0]; + var chain_letters = new Set(Object.keys(ths.options.pdb2seq).map(pdb_pos => pdb_pos[0])); for (let chain_letter of chain_letters) { - pos_seq = ths.options.pdb2seq[chain_letter+':'+ pos_pdb] - if (pos_seq) break + var pos_seq = ths.options.pdb2seq[chain_letter+':'+ pos_pdb]; + if (pos_seq) break; } - rest = group[3] - tooltip.innerText = "AA: " + "[" + aa + "]" + (pos_seq === undefined ? ' not alignment here! ' : pos_seq + 'seq/'+ pos_pdb + 'pdb') + rest + var rest = group[3]; + tooltip.innerText = "AA: " + "[" + aa + "] " + (pos_seq === undefined ? ' not alignment here! ' : pos_seq + ' seq / '+ pos_pdb + ' pdb') + rest; tooltip.style.bottom = (cp.y + 18) + "px"; tooltip.style.left = (cp.x + 15) + "px"; tooltip.style.display = "block"; @@ -64,203 +67,205 @@ require_js(["/js-find/ngl/ngl.min.js"], function() {y: this.options.stage.mouseControls.remove("clickPick"); this.options.stage.signals.clicked.add(function(pickingProxy){ if(pickingProxy && (pickingProxy.atom || pickingProxy.bond)){ + var buttons = ths.options.stage.mouseControls.mouse.buttons; + // right click + if (buttons == 2) { + // implement here the context menu when right button is pressed + } else { var atom = pickingProxy.atom || pickingProxy.closestBondAtom; var cp = pickingProxy.canvasPosition; - group = atom.qualifiedName().match(/\[(\w+)\](\d+)(.+)/) - aa = group[1] - pos_pdb = group[2] - first_pos_chain = Object.keys(ths.options.pdb2seq)[0] - chain_letters = new Set(Object.keys(ths.options.pdb2seq).map((pdb_pos) => pdb_pos[0])) + var group = atom.qualifiedName().match(/\[(\w+)\](\d+)(.+)/); + var aa = group[1]; + var pos_pdb = group[2]; + var first_pos_chain = Object.keys(ths.options.pdb2seq)[0]; + var chain_letters = new Set(Object.keys(ths.options.pdb2seq).map(pdb_pos => pdb_pos[0])); for (let chain_letter of chain_letters) { - pos_seq = ths.options.pdb2seq[chain_letter+':'+ pos_pdb] - if (pos_seq) break + var pos_seq = ths.options.pdb2seq[chain_letter+':'+ pos_pdb]; + if (pos_seq) break; } - rest = group[3] - tooltip.innerText = "AA: " + "[" + aa + "]" + (pos_seq === undefined ? ' not alignment here! ' : pos_seq + 'seq/'+ pos_pdb + 'pdb') + rest + var rest = group[3] + tooltip.innerText = "AA: " + "[" + aa + "] " + (pos_seq === undefined ? ' not alignment here! ' : pos_seq + ' seq / '+ pos_pdb + ' pdb') + rest; tooltip.style.bottom = (cp.y + 18) + "px"; tooltip.style.left = (cp.x + 15) + "px"; tooltip.style.display = "block"; - ths.mark_position(pos_pdb, 'pdb') + if (pos_seq === undefined) return + ths.mark_positions_by_colors('click', [pos_pdb], 'pdb', 'red') + } } }); }, + /** + * Viewer constructor + */ _init: function() { this.options.component_promise = new Promise((resolve, reject) => { if (this.options.pdb_url.match(/interactome3d/)) { - this.options.pdb_url_download ='/interactome3d?=pdb'+this.options.pdb_url + this.options.pdb_url_download ='/interactome3d?=pdb'+this.options.pdb_url; } else { - this.options.pdb_url_download = this.options.pdb_url + this.options.pdb_url_download = this.options.pdb_url; } - this.options.stage.removeAllComponents() - this._reset() + this.options.stage.removeAllComponents(); + this._reset(); + this._update_color_scheme(); + var ths = this; // only first model in NMR this.options.stage.loadFile(this.options.pdb_url_download, {ext: 'pdb', firstModelOnly: true}).then(function(component){ - //if(component.type !== "structure") return; component.addRepresentation("cartoon", { - //color: "sstruc" , + color: ths.options.colorSchemeNGL, sele: "protein and .CA", assembly: 'AU', multipleBond: true }); component.autoView(); - resolve(component) - }) - }) - this.clear() + resolve(component); + }); + }); + this.options.stage.setSpin(false); + this.options.stage.rockAnimation.pause(true) }, - _draw: function(){ - this.options.colorScheme_promise = new Promise((resolve, reject) => { - var colorScheme = [] - if (this.options.marks['mark'].size > 0) { - colorScheme.push([this.options.default_colors['mark'], Array.from(this.options.marks['mark']).join(' or ')]) - } - if (this.options.marks['feature'].size > 0) { - colorScheme.push([this.options.default_colors['feature'], Array.from(this.options.marks['feature']).join(' or ')]) - } - if (Object.keys(this.options.marks).length > 4) { - for (let key of Object.keys(this.options.marks)){ - if (!Object.keys(this.options.default_colors).includes(key)) { - colorScheme.push([key, Array.from(this.options.marks[key]).join(' or ')]) - } - } - } - if (this.options.marks['align'].size > 0) { - colorScheme.push([this.options.default_colors['align'], Array.from(this.options.marks['align']).join(' or ')]) - } - if (this.options.marks['original_alignment'].size > 0) { - colorScheme.push([this.options.default_colors['original_alignment'], Array.from(this.options.marks['original_alignment']).join(' or ')]) + /** + * For drawing colors in order + */ + _update_layers_sequence: function(layer) { + var i = this.options.layers_sequence.indexOf(layer); + if(i != -1) this.options.layers_sequence.splice(i, 1); + if (layer != this.options.layers_sequence[0]) this.options.layers_sequence.unshift(layer); + }, + + /** + * Generate the color scheme to be drawn by the viewer + */ + _update_color_scheme: function(){ + this.options.colorScheme = []; + + for (let layer of this.options.layers_sequence) { + for(let color of Object.keys(this.options.marks[layer])) { + if (this.options.marks[layer][color].size == 0) continue; + this.options.colorScheme.push([color, Array.from(this.options.marks[layer][color]).join(' or ')]); } - colorScheme.push([this.options.default_colors['default'], '*']) - resolve(colorScheme) - }) - this.options.colorScheme_promise.then((colorScheme) => { - this.options.colorScheme = NGL.ColormakerRegistry.addSelectionScheme(colorScheme, "colorScheme" ); - this.options.component_promise.then((component) => {component.addRepresentation("cartoon", { - color: this.options.colorScheme, - sele: "protein and .CA", - assembly: 'AU', - }) - component.autoView() - }) - }) + } + + this.options.colorScheme.push([this.options.default_colors['default'], '*']); + this.options.colorSchemeNGL = NGL.ColormakerRegistry.addSelectionScheme(this.options.colorScheme, "colorScheme"); + }, + + draw: function(){ + this._update_color_scheme(); + var ths = this; + this.options.component_promise.then(component => {component.addRepresentation("cartoon", { + color: ths.options.colorSchemeNGL, + sele: "protein and .CA", + assembly: 'AU', + }); + //component.autoView(); + }); }, _reset: function(){ - this._reset_marks() + this.options.marks = {}; + this.options.marks['original_alignment'] = {} + this.options.marks['original_alignment'][this.options.default_colors['original_alignment']] = new Set(); + this.options.layers_sequence = []; + for (let pos of Object.keys(this.options.seq2pdb)) { + this.options.marks['original_alignment'][this.options.default_colors['original_alignment']].add(this._get_pos(pos, 'seq')); + } + this._update_layers_sequence('original_alignment'); }, - clear: function(){ - this._reset() - this._draw() + clear_all: function(){ + this._reset(); + this.draw(); }, - _get_pos: function(pos, seq_or_pdb) { - if (seq_or_pdb == 'seq') { - chains = this.options.seq2pdb[pos] - pos = chains[0].match(/[A-Z]:(\d+)/)[1] + clear_by_layers: function(layers){ + for (let layer of layers) { + this.options.marks[layer] = new Set(); } - return pos + this.draw(); }, - _reset_marks: function() { - this.options.marks = {} - this.options.marks['mark'] = new Set() - this.options.marks['feature'] = new Set() - this.options.marks['align'] = new Set() - this.options.marks['original_alignment'] = new Set() - for (let pos of Object.keys(this.options.seq2pdb)) { - this.options.marks['original_alignment'].add(this._get_pos(pos, 'seq')) + clear_by_layer: function(layer){ + this.clear_by_layers([layer]) + }, + + /** + * Get the PDB position from a seq/pdb position + */ + _get_pos: function(pos, seq_or_pdb) { + if (seq_or_pdb == 'seq') { + chains = this.options.seq2pdb[pos]; + pos = chains[0].match(/[A-Z]:(\d+)/)[1]; } + return pos; }, - // mark, align, original_alignment, feature, color - _mark_position: function(pos, seq_or_pdb, color) { - color in this.options.marks || (this.options.marks[color] = new Set()); - this.options.marks[color].add(this._get_pos(pos, seq_or_pdb)) + _mark_position: function(layer, pos, seq_or_pdb, color) { + this.options.marks[layer][color].add(this._get_pos(pos, seq_or_pdb)); }, - mark_positions_by_colors: function(pos_list, seq_or_pdb, color_or_list) { + /* + * Default colors: mark, align, original_alignment, feature, color + */ + mark_positions_by_colors: function(layer, pos_list, seq_or_pdb, color_or_list, draw = true) { + this._update_layers_sequence(layer); + layer in this.options.marks || (this.options.marks[layer] = new Set());; if (Array.isArray(color_or_list)) { for (i = 0; i < pos_list.length; i++){ - this._mark_position(pos_list[i], 'seq', color_or_list[i]) + var color = color_or_list[i]; + color in this.options.marks[layer] || (this.options.marks[layer][color] = new Set());; + this._mark_position(layer, pos_list[i], seq_or_pdb, color); } } else { + var color = color_or_list + color in this.options.marks[layer] || (this.options.marks[layer][color] = new Set());; for (let pos of pos_list) { - this._mark_position(pos, 'seq', color_or_list) + this._mark_position(layer, pos, seq_or_pdb, color); } } - this._draw() - }, - - mark_position: function(pos, seq_or_pdb){ - this._mark_position(pos, seq_or_pdb, 'mark') - this._draw() - }, - - mark_positions: function(pos_list, seq_or_pdb) { - this.mark_positions_by_colors(pos_list, 'seq', 'mark') - }, - - mark_appris_features: function() { - pos_seq_features = [] - for (let feature of this.options.appris_features) { - pos_seq = feature['start'] - pos_pdb = this.options.seq2pdb[pos_seq] - if (feature['type'] == 'firestar' && pos_pdb) { - pos_seq_features.push(pos_seq) - } - } - this.mark_positions_by_colors(pos_seq_features, 'seq', 'feature') - }, - - align: function(){ - this._reset() - this.mark_positions_by_colors(Object.keys(this.options.seq2pdb), 'seq', 'align') + if (draw) this.draw(); }, color_mutation_density_subset: function(residues, residue_incidence) { - pos_list = [] + var pos_list = []; var log10_counts = []; for (let pos of residues) { if (this.options.seq2pdb[pos]){ var count = parseInt(residue_incidence[pos]) + 1; var log_count = Math.log(count) / Math.log(10); - log10_counts.push(log_count) - pos_list.push(pos) + log10_counts.push(log_count); + pos_list.push(pos); } } - log10_counts.push(0) + log10_counts.push(0); - colors = get_gradient(log10_counts, '#00FF00', '#FF0000'); + var colors = get_gradient(log10_counts, '#00FF00', '#FF0000'); - return {'pos_list': pos_list, 'colors': colors } + return {'pos_list': pos_list, 'colors': colors }; }, color_mutation_density: function(residue_incidence) { - console.log('color_mutation_density') - this._reset() - pos_colors = this.color_mutation_density_subset(Object.keys(residue_incidence), residue_incidence) - this.mark_positions_by_colors(pos_colors['pos_list'], 'seq', pos_colors['colors']) + pos_colors = this.color_mutation_density_subset(Object.keys(residue_incidence), residue_incidence); + this.mark_positions_by_colors('cosmic_mutation_density', pos_colors['pos_list'], 'seq', pos_colors['colors']); }, screenshot: function() { - this.options.stage.makeImage({factor: 1, antialias: true}).then((blob) => NGL.download(blob, this.options.pdb + ".png")) + this.options.stage.makeImage({factor: 1, antialias: true}).then(blob => NGL.download(blob, this.options.pdb + ".png")); }, resize: function() { - this.options.stage.autoView() + this.options.stage.autoView(); }, spin: function() { - this.options.stage.toggleSpin() + this.options.stage.toggleSpin(); }, fullscreen: function() { - this.options.stage.toggleFullscreen() + this.options.stage.toggleFullscreen(); } }) }) diff --git a/www/views/public/js/protein_tool.js b/www/views/public/js/protein_tool.js deleted file mode 100644 index 145e777..0000000 --- a/www/views/public/js/protein_tool.js +++ /dev/null @@ -1,42 +0,0 @@ -//$.widget("rbbt.protein_tool", { -// -// options: { }, -// -// _create: function() { -// this.element.addClass('protein_tool_init'); -// -// this.options.secondary_structure = this.element.find('.secondary_structure_tool'); -// this.options.jmol = this.element.find('.jmol_tool'); -// }, -// -// _redirect_function: function(name){ -// var args = arguments; -// -// this.options.secondary_structure.secondary_structure_tool(name, args[1], args[2]); -// this.options.secondary_structure.secondary_structure_tool(name, args[1], args[2], args[3], args[4]); -// -// if (this.options.jmol !== undefined && this.options.jmol.jmol_tool('is_pdb_loaded')){ -// console.log(this.options.jmol.jmol_tool('loaded_pdb')) -// this.options.jmol.jmol_tool(name, args[1], args[2], args[3], args[4]) -// } -// }, -// -// clear: function(){ -// this._redirect_function('clear'); -// }, -// -// mark_position: function(position, color){ -// if (typeof position == 'string') position = parseInt(position) -// this._redirect_function('mark_position', position, color); -// }, -// -// mark_positions: function(positions, color){ -// this._redirect_function('mark_positions', positions, color); -// }, -// -// mark_region: function(start, end, color){ -// this._redirect_function('mark_region', start, end, color); -// }, -//}); - - diff --git a/www/views/public/js/rbbt.protein_tool.js b/www/views/public/js/rbbt.protein_tool.js index 90726a5..fa7e524 100644 --- a/www/views/public/js/rbbt.protein_tool.js +++ b/www/views/public/js/rbbt.protein_tool.js @@ -16,11 +16,18 @@ rbbt.sequence.mark_position = function(element, position, color){ rbbt.svg = {} -rbbt.svg.clear = function(element){ +rbbt.svg.clear = function(element, layer=''){ var svg = element.find('svg'); - var vlines = svg.find('.rbbt-vline') + var vlines; + var vregions; + if (layer) { + vlines = svg.find('.rbbt-vline.' + layer) + vregions = svg.find('.rbbt-region.' + layer) + } else { + vlines = svg.find('.rbbt-vline') + vregions = svg.find('.rbbt-region') + } vlines.remove() - var vregions = svg.find('.rbbt-region') vregions.remove() } @@ -29,10 +36,10 @@ rbbt.svg.position_offset = function(element, position){ var width = parseInt(svg.attr('width')); var start = parseInt(svg.find('rect.ac').attr('x')); var seq_len = parseInt(element.attr('data-sequence_length')); - return start + position * (width - start)/seq_len + return start + position * (width - start)/seq_len } -rbbt.svg.mark_position = function(element, position, color){ +rbbt.svg.mark_position = function(element, position, color, layer=''){ if (undefined === color) color = 'red'; var svg = element.find('svg'); @@ -45,36 +52,40 @@ rbbt.svg.mark_position = function(element, position, color){ line.setAttributeNS(null, "y1", 5); line.setAttributeNS(null, "x2", offset); line.setAttributeNS(null, "y2", height - 5); - line.setAttributeNS(null, "class", 'rbbt-vline'); + line.setAttributeNS(null, "class", 'rbbt-vline' + (layer ? ' ' + layer : '')); line.setAttributeNS(null, "style", "stroke:" + color + ";opacity:0.5;stroke-width:1;"); svg.append(line); } -rbbt.svg.mark_positions = function(element, positions, color){ +rbbt.svg.mark_positions = function(element, positions, color, layer=''){ for (var i = 0; i < positions.length; i++){ - var position = positions[i] + var position = positions[i]; var seq_len = parseInt(element.attr('data-sequence_length')); jitter = Math.ceil(seq_len / 1000) + 2; position = position + Math.floor(Math.random() * 2 * jitter) -jitter + 1 - rbbt.svg.mark_position(element, position, color) + if (Array.isArray(color)) { + rbbt.svg.mark_position(element, position, color[i], layer); + } else { + rbbt.svg.mark_position(element, position, color, layer); + } } } -rbbt.svg.mark_region = function(element, first, last, color){ +rbbt.svg.mark_region = function(element, first, last, color, layer=''){ var svg = element.find('svg') - var width = parseInt(svg.attr('width')); + var width = parseInt(svg.attr('width')); var height = parseInt(svg.attr('height')); - var start = parseInt(svg.find('rect.ac').attr('x')); + var start = parseInt(svg.find('rect.ac').attr('x')); var seq_len = parseInt(element.attr('data-sequence_length')); - var offset_start = rbbt.svg.position_offset(element, first) - var offset_end = rbbt.svg.position_offset(element, last) + var offset_start = rbbt.svg.position_offset(element, first); + var offset_end = rbbt.svg.position_offset(element, last); var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); rect.setAttributeNS(null, "x", offset_start); - rect.setAttributeNS(null, "class", 'rbbt-region'); + rect.setAttributeNS(null, "class", 'rbbt-region' + (layer ? ' ' + layer : '')); rect.setAttributeNS(null, "y", 10); rect.setAttributeNS(null, "width", offset_end - offset_start); rect.setAttributeNS(null, "height", height - 30); @@ -83,50 +94,25 @@ rbbt.svg.mark_region = function(element, first, last, color){ svg.append(rect); } -rbbt.svg.mark_aligned_region = function(element, map, color){ +rbbt.svg.mark_aligned_region = function(element, map, color, layer=''){ if (undefined === color) color = 'blue' var positions = [] for(seq_pos in map){ - positions.push(parseInt(seq_pos)) + positions.push(parseInt(seq_pos)); } positions = positions.sort(); - var last = -1 + var last = -1; var start = -1; for (var i = 0; i < positions.length; i++){ if (positions[i] != last + 1){ - if (start != -1) { rbbt.svg.mark_region(element, start, last, color); } - start = positions[i] + if (start != -1) { rbbt.svg.mark_region(element, start, last, color, layer); } + start = positions[i]; } - last = positions[i] + last = positions[i]; } - if (start != -1) { rbbt.svg.mark_region(element, start, last, color); } -} - - -rbbt.jmol = {} -rbbt.jmol.clear = function(element){ - element.jmol_tool('clear') -} - -rbbt.jmol.loaded = function(element){ - return(element.jmol_tool('is_pdb_loaded')); + if (start != -1) { rbbt.svg.mark_region(element, start, last, color, layer); } } -rbbt.jmol.mark_position = function(element, position, color){ - if (undefined === color) color = 'red'; - if (rbbt.jmol.loaded(element)){element.jmol_tool('mark_position', position, color)} -} - -rbbt.jmol.mark_positions = function(element, positions, color){ - if (undefined === color) color = 'red'; - if (rbbt.jmol.loaded(element)){element.jmol_tool('mark_positions', positions, color)} -} - -rbbt.jmol.color_positions = function(element, incidence){ - if (rbbt.jmol.loaded(element)){element.jmol_tool('color_mutation_density', incidence)} -} - - diff --git a/www/views/tools/protein_tool.haml b/www/views/tools/protein_tool.haml index 6f2eacd..4d6b686 100644 --- a/www/views/tools/protein_tool.haml +++ b/www/views/tools/protein_tool.haml @@ -1,7 +1,5 @@ - position = nil unless defined? position - sequence = protein.sequence -- jmol_id = 'Jmol-' << protein -- select_id = jmol_id + '-select' - pdbs = protein.pdbs - position = nil unless defined? position - colors = %w(red blue green yellow black white purple) @@ -29,15 +27,30 @@ - else - cosmic_residue_incidence[key] = samples.length +- iso2uni = Organism.protein_identifiers(protein.organism).index(:target => "UniProt/SwissProt Accession", :persist => true, :unnamed => true) +- iso2sequence = Organism.protein_sequence(protein.organism).tsv(:type => :single, :persist => true, :unnamed => true) +- cache_dir = Rbbt.var.Structure +- CORRECTED_FEATURES = cache_dir.corrected_features.find +- uniprot = iso2uni[protein] +- uniprot_features = {} +- unless uniprot.nil? + - sequence = iso2sequence[protein].chomp("*") + - unless sequence.nil? + - _other = {:uniprot => uniprot, :sequence => protein.sequence} + + - uniprot_features = Misc.insist do + - Persist.persist("Corrected UniProt features", :marshal, :persist => true, :lock => {:max_age => 0, :suspend => 0, :refresh => false}, :dir => CORRECTED_FEATURES, :other => _other) do + - Structure.corrected_uniprot_features(uniprot, protein.sequence) + +- appris_features = Structure.appris_features(protein) + :sass .protein_tool .controls float: right margin-bottom: -2px - .ui.input, .ui.buttons + .ui.buttons padding: 0px - input - width: 100% !important .tabular.menu width: 300px !important @@ -49,8 +62,47 @@ %input(placeholder='Position' type='text' name='position') .ui.buttons .mark.submit.ui.button Mark + .ui.dropdown.button.item#marks + Marks + %i.dropdown.icon + .vertical.menu#all + .ui.dropdown.item#appris + .ui.checkbox#appris + %input(name="public" type="checkbox") + %label + %i.down.caret.icon + Appris Features + .menu.compact(style='position: relative; right: 100px;') + - appris_features.map{|f| f[:type]}.uniq.sort.each do |appris_type| + .item.appris_feature(id='#{appris_type}') + .ui.checkbox.toggle.appris_feature(id='#{appris_type}') + %input(name='public' type="checkbox") + %label= appris_type + .ui.dropdown.scrolling.item#uniprot + .ui.modal.fullscreen#uniprot + .header.center + Uniprot Features + .content + Only existing features in PDB are shown + .actions + .ui.cancel.button Close + .ui.checkbox#uniprot + %input(name="public" type="checkbox") + %label + %i.down.caret.icon + Uniprot Features + %i.icon.blue.help.circle#uniprot + .menu.compact(style='position: relative; right: 100px') + - uniprot_features.map{|f| f[:type]}.uniq.sort.each do |uniprot_type| + .item.uniprot_feature(id='#{uniprot_type}') + .ui.checkbox.toggle.uniprot_feature(id='#{uniprot_type}') + %input(name='public' type="checkbox") + %label= uniprot_type + .item.cosmic + .ui.checkbox.cosmic + %input(name="public" type="checkbox") + %label COSMIC mutations .clear.submit.ui.button Clear - .align.submit.ui.button Align .ui.tabular.menu.top.attached .item.active(data-tab=Sequence) Sequence @@ -97,25 +149,12 @@ - tabs.each do |tabfile| - name = File.basename(tabfile).sub('_tab.haml','') .very.basic.ui.segment.tab(class=name data-tab=name) - = partial_render(tabfile, :associations => associations, :id => id, :select_id => select_id, :pdbs => pdbs, :uniprot => uniprot, :protein => protein, :position => position, :cosmic_residue_incidence => cosmic_residue_incidence) - - -#.jmol.very.basic.ui.segment.tab(data-tab='JMol') - = fragment do - = partial_render('tools/protein_tool/jmol_tab', :select_id => select_id, :pdbs => pdbs, :uniprot => uniprot, :protein => protein, :position => position, :cosmic_residue_incidence => cosmic_residue_incidence) - + = partial_render(tabfile, :associations => associations, :id => id, :pdbs => pdbs, :uniprot => uniprot, :protein => protein, :position => position, :cosmic_residue_incidence => cosmic_residue_incidence, :appris_features => appris_features, :uniprot_features => uniprot_features) -#.COSMIC.very.basic.ui.segment.tab(data-tab='COSMIC') = fragment do = partial_render('tools/protein_tool/COSMIC_tab', :protein => protein, :associations => associations, :id => id) -:sass - .JMol:not(.active) - display: block !important - visibility: hidden - height: 0px - margin: 0px - padding: 0px - :deferjs $('.tabular.menu .item').tab() diff --git a/www/views/tools/protein_tool/Mutations&Features_tab.haml b/www/views/tools/protein_tool/Mutations&Features_tab.haml new file mode 100644 index 0000000..cfbd2da --- /dev/null +++ b/www/views/tools/protein_tool/Mutations&Features_tab.haml @@ -0,0 +1,19 @@ +- tabs = glob_all('tools/protein_tool/mutations&features_tabs/*_tab.haml').uniq.sort +.ui.button Update selected +.ui.divider.hidden +.ui.secundary.menu.compact + - tabs.each do |tabfile| + - name = File.basename(tabfile).sub('_tab.haml','') + .item(id=name data-tab=name)= name + +- tabs.each do |tabfile| + - name = File.basename(tabfile).sub('_tab.haml','') + .very.basic.ui.segment.tab(class=name data-tab=name) + = partial_render(tabfile, :associations => associations, :id => id, :pdbs => pdbs, :uniprot => uniprot, :protein => protein, :position => position, :cosmic_residue_incidence => cosmic_residue_incidence, :appris_features => appris_features, :uniprot_features => uniprot_features) + +:deferjs + $('.item#COSMIC').addClass('active') + $('.tab.COSMIC').addClass('active') + $('.secundary.menu .item').tab() + + diff --git a/www/views/tools/protein_tool/PDB-Viewer_tab.haml b/www/views/tools/protein_tool/PDB-Viewer_tab.haml index 132bc7a..9123a69 100644 --- a/www/views/tools/protein_tool/PDB-Viewer_tab.haml +++ b/www/views/tools/protein_tool/PDB-Viewer_tab.haml @@ -45,38 +45,55 @@ - Open.write(@step.file('pdb_alignments'), pdb_alignments.to_json) - -- iso2uni = Organism.protein_identifiers(protein.organism).index(:target => "UniProt/SwissProt Accession", :persist => true, :unnamed => true) -- iso2sequence = Organism.protein_sequence(protein.organism).tsv(:type => :single, :persist => true, :unnamed => true) -- cache_dir = Rbbt.var.Structure -- CORRECTED_FEATURES = cache_dir.corrected_features.find -- uniprot = iso2uni[protein] -- uniprot_features = {} -- unless uniprot.nil? - - sequence = iso2sequence[protein].chomp("*") - - unless sequence.nil? - - _other = {:uniprot => uniprot, :sequence => protein.sequence} - - - uniprot_features = Misc.insist do - - Persist.persist("Corrected UniProt features", :marshal, :persist => true, :lock => {:max_age => 0, :suspend => 0, :refresh => false}, :dir => CORRECTED_FEATURES, :other => _other) do - - Structure.corrected_uniprot_features(uniprot, protein.sequence) - .ui.field .NGL_viewer .ui.form .inline.fields .field - %H1 NGL viewer + .ui.compact.menu.right + .ui.dropdown.item + Viewer Control + %i.dropdown.icon + .menu + .item#screenshot Screenshot + .item#resize Resize + .item#spin Spin + .item#fullscreen FullScreen + .ui.dropdown.item.compact#marks_pdb + Mark PDB + %i.dropdown.icon + .right.menu#all + .item.align + .ui.checkbox.align + %input(name="public" type="checkbox") + %label Align + .item.interfaces + .ui.checkbox.interfaces + %input(name="public" type="checkbox") + %label Interfaces + .item#inputs + .ui.input + %input(placeholder='Position' type='text' name='neighbours') + %input(placeholder='5 angstroms' type='text' name='angstroms') + .ui.button.mark.neighbours + Mark neighbours + .item#inputs + .ui.input + %input(placeholder='3 clusters' type='text' name='clusters') + .text(contenteditable='true') + .ui.button.mark.clusters + Mark clusters + -#.ui.item#clear(style='cursor: pointer;') Clear .field .ui.floating.dropdown.labeled.icon.button#menu_pdb %i.dropdown.icon %span.text#selected_pdb(selected="selected") Select a PDB - .menu + .menu.right#select_pdb .ui.icon.input %input#input_pdb(placeholder="PDB | pos | posN..posN+1" type="text") %i.search.icon - .scrolling.menu + .menu.right.scrolling .divider .header RCSB @@ -117,69 +134,22 @@ - info[:region] = [pos.min, pos.max] * ".." .item.interaction#pdb(attr-pdb=filename attr-url=url)= "#{info[:pair]} (#{info[:region]}) #{type} - #{filename}" .window#ngl_viewport{:style => "width:100%; height:800px;"} - .ui.hidden.divider - .ui.button.screenshot - Screenshot - .ui.button.resize - Resize - .ui.button.spin - Spin - .ui.button.fullscreen - Fullscreen - .ui.hidden.divider - .ui.styled.fluid.accordion - .title - %i.dropdown.icon - %strong.ui.header Mark regions - .content - .ui.basic.left.aligned.segment - .ui.input - %input(placeholder='' type='text' name='neighbours') - .ui.input - %input(placeholder='5 angstroms' type='text' name='angstroms') - .ui.button.mark.neighbours - Mark neighbours - .ui.hidden.divider - .ui.input - %input(placeholder='3 clusters' type='text' name='clusters') - .ui.button.mark.clusters - Mark clusters - .ui.hidden.divider - .ui.styled.fluid.accordion - .title - %i.dropdown.icon - %strong.ui.header Mark features - .content - .ui.basic.left.aligned.segment - .ui.form - .inline.fields - .field - .ui.segment - .ui.toggle.checkbox.disabled.appris - %input(name="public" type="checkbox") - %label Appris features - .field - .ui.segment - .ui.toggle.checkbox.disabled.uniprot - %input(name="public" type="checkbox") - %label Uniprot Features - .field - .ui.segment - .ui.toggle.checkbox.disabled.cosmic - %input(name="public" type="checkbox") - %label COSMIC mutations - .field - .ui.segment - .ui.toggle.checkbox.disabled.interfaces - %input(name="public" type="checkbox") - %label Interfaces - .ui.hidden.divider - :deferjs - $('#menu_pdb').dropdown() + $('.menu#all').hide() + + $('.menu#all, .menu#select_pdb').removeClass('left') + $('.item#marks_pdb, .dropdown#menu_pdb').click(function(){$('.menu#all, .menu#select_pdb').removeClass('left');setTimeout(function(){$('.menu#all, .menu#select_pdb').removeClass('left');}, 0)}) + + $('i.help.circle.icon#uniprot').click(function(event){ + event.stopPropagation(); + $('.ui.modal#uniprot').modal('show') + }); + + $('.item#inputs').click(function(event){event.stopPropagation()}) + $('.dropdown').dropdown(); $('.ui.accordion').accordion(); @@ -191,269 +161,397 @@ }; })(); - $('#input_pdb').keyup(function(){ + $('#input_pdb').keyup(function(){ delay(function(){ - input = $('#input_pdb').val().trim() + var input = $('#input_pdb').val().trim(); $('.item#pdb ').each(function(i, e){ - $(this).show() - pdb_description = $(this).text() - group_pdb = pdb_description.match(/(\d+)\.\.(\d+)/) - start_pdb = Number(group_pdb[1]) - end_pdb = Number(group_pdb[2]) + $(this).show(); + var pdb_description = $(this).text(); + var group_pdb = pdb_description.match(/(\d+)\.\.(\d+)/); + var start_pdb = Number(group_pdb[1]); + var end_pdb = Number(group_pdb[2]); if (input.match(/^\d+$/)) { - input_pos = Number(input) + input_pos = Number(input); if (input_pos < start_pdb || end_pdb < input_pos){ - $(this).hide() + $(this).hide(); } } else if (input.match(/^(\d+)\.\.(\d+)$/)){ - group_input = input.match(/(\d+)\.\.(\d+)/) - start_input = Number(group_input[1]) - end_input = Number(group_input[2]) + group_input = input.match(/(\d+)\.\.(\d+)/); + start_input = Number(group_input[1]); + end_input = Number(group_input[2]); if (start_input > end_input || start_input < start_pdb || end_pdb < end_input){ - $(this).hide() + $(this).hide(); } } else{ if (!pdb_description.toLowerCase().match(input.toLowerCase())){ - $(this).hide() + $(this).hide(); } } }) }, 450); }) - var pdb_alignments_url = add_parameter(window.location.href, '_fragment', 'pdb_alignments') - rbbt.ajax({url:pdb_alignments_url}).then(function(pdb_alignments){ - require_js(["/js-find/rbbt.protein_tool.js","/js-find/ngl.js"], function(){ - var position = #{position ? position : "undefined"} - var cosmic_residue_incidence = #{cosmic_residue_incidence.to_json} - var appris_features = {} - var pdb2seq = {} - var seq2pdb = {} - var pdb - var url - - rbbt.ajax({url:"/appris_features?isoform=#{protein}"}).then(function(features){ - appris_features = features - }) + var position = #{position ? position : "undefined"}; + var cosmic_residue_incidence = #{cosmic_residue_incidence.to_json}; + var default_colors = {'mark': 'red', 'original_alignment': 'darkslategray', 'align': 'blue', 'feature': 'purple', 'default': 'lightgray'}; + var svg_element = $('.svg') + var uniprot_features = #{uniprot_features.to_json}; + var appris_features = #{appris_features.to_json}; + var pdb2seq = {}; + var seq2pdb = {}; + var pdb; + var url; $('.item#pdb').click(function(){ - var selected_pdb = $(this) + var selected_pdb = $(this); pdb = selected_pdb.attr('attr-pdb'); url = selected_pdb.attr('attr-url'); - seq2pdb = pdb_alignments[pdb] - keys = Object.keys(seq2pdb) - keys = keys.map(Number); - for (number = 0; number < keys.length; number++) { - key = keys[number] - values = seq2pdb[key] - for (num = 0; num < values.length; num++) { - value = values[num] - pdb2seq[value] = key - } - } - $('.ui.checkbox.appris').removeClass('disabled') - $('.ui.checkbox.uniprot').removeClass('disabled') - $('.ui.checkbox.cosmic').removeClass('disabled') - $('.ui.checkbox.interfaces').removeClass('disabled') - rbbt.ngl = $('.NGL_viewer').ngl_tool({pdb_url: url, pdb: pdb, seq2pdb: seq2pdb, pdb2seq: pdb2seq, appris_features: appris_features}) - }) - - body.on('click', '.ui.checkbox.cosmic', function(){ - if ($('.ui.checkbox.cosmic').hasClass('checked')) { - rbbt.ngl.ngl_tool('color_mutation_density', cosmic_residue_incidence) - } else { - if(typeof rbbt.ngl == 'undefined') return - rbbt.ngl.ngl_tool('clear') - } - }) + var pdb_alignments_url = add_parameter(window.location.href, '_fragment', 'pdb_alignments') + rbbt.ajax({url:pdb_alignments_url}).then(function(pdb_alignments){ + seq2pdb = pdb_alignments[pdb]; + keys = Object.keys(seq2pdb).map(Number); + for (let key of keys) { + var values = seq2pdb[key]; + for (let value of values) { + pdb2seq[value] = key; + } + } - body.on('click', '.ui.checkbox.uniprot', function(){ - if ($('.ui.checkbox.uniprot').hasClass('checked')) { - uniprot_features = #{uniprot_features.to_json} - relevant = ["DISULFID", "DNA_BIND", "METAL", "INTRAMEM", "CROSSLNK", "MUTAGEN"] - relevant_uniprot_features = uniprot_features.filter((uniprot_feature) => relevant.includes(uniprot_feature["type"])) - pos_list = relevant_uniprot_features.filter((uniprot_feature) => Object.keys(seq2pdb).map(Number).includes(uniprot_feature['start'])).map((uniprot_feature) => uniprot_feature['start']) - rbbt.ngl.ngl_tool('mark_positions_by_colors', pos_list, 'seq', 'orange') - } - }) + $('.item').removeClass('active selected'); + $('.ui.checkbox').removeClass('hidden').checkbox('set unchecked'); + $('.item#appris, .item#uniprot, .item.cosmic, .item.interfaces').show() - body.on('click', '.ui.checkbox.interfaces', function(){ - if ($('.ui.checkbox.interfaces').hasClass('checked')) { - positions= [] rbbt.job("Structure", "neighbour_map", {distance: 5, pdb: url}, 'true').then(function(all_neighbours_pdb){ - pos_list = [] + var pos_list = []; for (let pos_pdb of Object.keys(all_neighbours_pdb)) { if (pdb2seq[pos_pdb]) { - different_chains = new Set(all_neighbours_pdb[pos_pdb].map((p) => p[0])) - if (different_chains.size > 1) { - pos_list.push(pdb2seq[pos_pdb]) + var different_chains = new Set(all_neighbours_pdb[pos_pdb].map(p => p.split(':')[0])); + var seq_pos = pdb2seq[pos_pdb] + if (different_chains.size > 1 && seq_pos) { + pos_list.push(seq_pos); } } } - rbbt.ngl.ngl_tool('mark_positions_by_colors', pos_list ,'seq', 'red') - }) - } else { - if(typeof rbbt.ngl == 'undefined') return - rbbt.ngl.ngl_tool('clear') - } - }) + if (pos_list.length == 0) $('.item.interfaces').hide() + }); + + //feature_names = ['appris', 'uniprot'] + //all_features = [] + //all_features.push(appris_features) + //all_features.push(uniprot_features) + //all_features.forEach(function(features,index){ + // features_by_pdb = features.filter(f => seq2pdb[f['start']] && seq2pdb[f['end']]); + // if (features_by_pdb.length == 0) { $('.item#'+ feature_names[index]).hide(); return} + // $('.item.' + feature_names[index] + '_feature').hide(); + // types = Array.from(new Set(features_by_pdb.map(f => f['type']))).sort(); + // types.forEach(function(type){ + // if (features_by_pdb.filter(f => f['type'] == type).length != 0) { + // $('.item.'+ feature_names[index] + '_feature#' + type).show(); + // } + // }); + //}); + if (cosmic_residue_incidence.length == 0) $('.item.cosmic').hide() + + rbbt.ngl = $('.NGL_viewer').ngl_viewer({pdb_url: url, pdb: pdb, seq2pdb: seq2pdb, pdb2seq: pdb2seq, default_colors: default_colors}) + }); + }); + + $('.item.cosmic').click(function(event){ + event.stopPropagation() + var $checkbox = $('.ui.checkbox.cosmic') + $(this).toggleClass('on') + if ($(this).hasClass('on')) { + var pos_list = []; + var log10_counts = []; + for (let pos of Object.keys(cosmic_residue_incidence)) { + var count = parseInt(cosmic_residue_incidence[pos]) + 1; + var log_count = Math.log(count) / Math.log(10); + log10_counts.push(log_count); + pos_list.push(pos); + } - body.on('click', '.ui.checkbox.appris', function(){ - if ($('.ui.checkbox.appris').hasClass('checked')) { - rbbt.ngl.ngl_tool('mark_appris_features') - } else { - var link = $(this); - var controls = link.parents('.controls').first(); - var protein_tool = controls.parents('.protein_tool').first(); - var sequence_element = protein_tool.find('.sequence').first(); - var svg_element = protein_tool.find('.svg').first(); + log10_counts.push(0); - rbbt.sequence.clear(sequence_element) - rbbt.svg.clear(svg_element) + var colors = get_gradient(log10_counts, '#00FF00', '#FF0000'); + rbbt.svg.mark_positions(svg_element, pos_list.map(Number), colors, 'cosmic'); + if (typeof rbbt.ngl != 'undefined') { + var pos_colors = rbbt.ngl.ngl_viewer('color_mutation_density_subset', Object.keys(seq2pdb), cosmic_residue_incidence); + rbbt.svg.mark_positions(svg_element, pos_colors['pos_list'].map(Number), pos_colors['colors'], 'cosmic'); + $checkbox.checkbox('set checked') + rbbt.ngl.ngl_viewer('color_mutation_density', cosmic_residue_incidence); + } + } else { + $checkbox.checkbox('set unchecked') + rbbt.svg.clear(svg_element, 'cosmic') + if(typeof rbbt.ngl == 'undefined') return; + rbbt.ngl.ngl_viewer('clear_by_layer', 'cosmic_mutation_density'); + } + }); + + $('.item#uniprot, .item#appris').click(function(event){ + event.stopPropagation() + var id = $(this).prop('id') + var features = id == 'appris' ? appris_features : uniprot_features + var feature_name = id == 'appris' ? 'appris_feature' : 'uniprot_feature' + var color = id == 'appris' ? 'deeppink' : 'purple' + $(this).toggleClass('on'); + if ($(this).hasClass('on')) { + $('.ui.checkbox#' + id).checkbox('set checked') + $('.item.' + feature_name).addClass('on') + $('.ui.checkbox.' + feature_name + ':visible').checkbox('set checked') + var layers_id = [] + $('.ui.checkbox.' + feature_name + '.checked').each(function(){layers_id.push($(this).prop('id'))}) + //var relevant = ["DISULFID", "DNA_BIND", "METAL", "INTRAMEM", "CROSSLNK", "MUTAGEN"]; + var checked_features = features.filter(feature => layers_id.includes(feature["type"])); + layers_id.forEach(function(layer_id){ + var filtered_features = features.filter(feature => feature['type'] == layer_id) + var pos_list = new Set() + filtered_features.forEach(function(filtered_feature) { + var size = filtered_feature['end'] - filtered_feature['start'] + for (var i = 0; i <= size; i++) pos_list.add(filtered_feature['start'] + i) + }); + rbbt.svg.mark_positions(svg_element, Array.from(pos_list).map(Number), color, id + layer_id); + seq_pos = Array.from(pos_list).filter(pos => seq2pdb[pos]) + if(typeof rbbt.ngl != 'undefined') rbbt.ngl.ngl_viewer('mark_positions_by_colors', id + layer_id, seq_pos, 'seq', color, false); + }); + if(typeof rbbt.ngl != 'undefined') rbbt.ngl.ngl_viewer('draw') + } else { + $('.item.' + feature_name).removeClass('on') + $('.ui.checkbox#' + id).checkbox('set unchecked') + var layers_id = [] + $('.ui.checkbox.' + feature_name + '.checked').each(function(){layers_id.push(id + $(this).prop('id'))}) + $('.ui.checkbox.' + feature_name + '.checked').checkbox('set unchecked') + for (let layer_id of layers_id) rbbt.svg.clear(svg_element, layer_id) + if(typeof rbbt.ngl != 'undefined') { + rbbt.ngl.ngl_viewer('clear_by_layers', layers_id) + } + } + $('.ui.dropdown#marks').dropdown('show') + }); + + $('.item.appris_feature, .item.uniprot_feature').click(function(event){ + event.stopPropagation(); + var layer_id = $(this).prop('id'); + var feature_name = $(this).hasClass('appris_feature') ? 'appris_feature' : 'uniprot_feature' + var feature_id = $(this).hasClass('appris_feature') ? 'appris' : 'uniprot' + var $checkbox = $('.ui.checkbox.' + feature_name + '#' + layer_id) + var features = $(this).hasClass('appris_feature') ? appris_features : uniprot_features + var color = feature_id == 'appris' ? 'deeppink' : 'purple' + $(this).toggleClass('on') + if ($(this).hasClass('on')){ + $checkbox.checkbox('set checked') + $('.ui.checkbox#' + feature_id).checkbox('set indeterminate'); + if ($('.ui.checkbox.' + feature_name + '.checked').length == $('.ui.checkbox.' + feature_name + ':visible').length) { + $('.ui.item#' + feature_id).addClass('on') + $('.ui.checkbox#' + feature_id).checkbox('set checked') + } + var pos_list = new Set(); + features.filter(feature => feature['type'] == layer_id).forEach(function(feature) { + var size = feature['end'] - feature['start'] + for (var i = 0; i <= size; i++) { + pos_list.add(feature['start'] + i) + } + }); + rbbt.svg.mark_positions(svg_element, Array.from(pos_list).map(Number), color, feature_id + layer_id); + if(typeof rbbt.ngl == 'undefined') return + seq_pos = Array.from(pos_list).filter(pos => seq2pdb[pos]) + rbbt.ngl.ngl_viewer('mark_positions_by_colors', feature_id + layer_id, seq_pos, 'seq', color); + } else { + $checkbox.checkbox('set unchecked') + $('.ui.checkbox#' + feature_id).checkbox('set indeterminate'); + if ($('.ui.checkbox.' + feature_name + '.checked').length == 0) { + $('.ui.item#' + feature_id).removeClass('on') + $('.ui.checkbox#' + feature_id).checkbox('set unchecked') + } + rbbt.svg.clear(svg_element, feature_id + layer_id) if(typeof rbbt.ngl == 'undefined') return - rbbt.ngl.ngl_tool('clear') + rbbt.ngl.ngl_viewer('clear_by_layer', feature_id + layer_id) } - }) + }); + + $('.item.interfaces').click(function(event){ + event.stopPropagation() + var $checkbox = $('.ui.checkbox.interfaces') + if(typeof rbbt.ngl == 'undefined') { + $checkbox.checkbox('set unchecked') + $('.dropdown#marks_pdb').dropdown('hide') + $('.dropdown#marks_pdb').removeClass('left') + return alert("Select a PDB"); + } + $(this).toggleClass('on') + if ($(this).hasClass('on')){ + $checkbox.checkbox('set checked') + if(typeof rbbt.ngl == 'undefined') return + var pos_interfaces = [] + rbbt.job("Structure", "neighbour_map", {distance: 5, pdb: url}, 'true').then(function(all_neighbours_pdb){ + var pos_list = []; + for (let pos_pdb of Object.keys(all_neighbours_pdb)) { + if (pdb2seq[pos_pdb]) { + var different_chains = new Set(all_neighbours_pdb[pos_pdb].map(p => p.split(':')[0])); + var seq_pos = pdb2seq[pos_pdb] + if (different_chains.size > 1 && seq_pos) { + pos_list.push(seq_pos); + } + } + } + if (pos_list.length == 0) $('.item.interfaces').hide() + rbbt.svg.mark_positions(svg_element, Array.from(pos_list).map(Number), default_colors['marks'], 'interfaces'); + rbbt.ngl.ngl_viewer('mark_positions_by_colors', 'interfaces', pos_list,'seq', default_colors['mark']); + }); + } else { + $checkbox.checkbox('set unchecked') + rbbt.svg.clear(svg_element, 'interfaces') + rbbt.ngl.ngl_viewer('clear_by_layer', 'interfaces'); + } + }); - $('.clear.submit').click(function(){ + $('.clear.submit, .item#clear').click(function(){ + $('.dropdown.item').dropdown('hide') var link = $(this); var controls = link.parents('.controls').first(); var protein_tool = controls.parents('.protein_tool').first(); var sequence_element = protein_tool.find('.sequence').first(); var svg_element = protein_tool.find('.svg').first(); - rbbt.sequence.clear(sequence_element) - rbbt.svg.clear(svg_element) - if(typeof rbbt.ngl == 'undefined') return alert("Select a PDB") - rbbt.ngl.ngl_tool('clear') + rbbt.sequence.clear(sequence_element); + rbbt.svg.clear(svg_element); + $('.item').removeClass('active selected'); + $('.ui.checkbox').checkbox('set unchecked'); + if(typeof rbbt.ngl == 'undefined') return + rbbt.ngl.ngl_viewer('clear_all'); }) - $('.mark.submit').click(function(){ + $('.mark.submit, .mark.position').click(function(){ var link = $(this); - var controls = link.parents('.controls').first(); - var protein_tool = controls.parents('.protein_tool').first(); - var sequence_element = protein_tool.find('.sequence').first(); + var segment = link.parents('.segment').first(); + var protein_tool = segment.parents('.protein_tool').first(); + var sequence_element = protein_tool.find('.sequence').first(); var svg_element = protein_tool.find('.svg').first(); - var position = parseInt(controls.find('input[name=position]').val()); + var position = parseInt(segment.find('input[name=position]').val()); - if (! position > 0) return alert("No position specified") + if (! position > 0) return alert("No position specified"); - first = Object.keys(seq2pdb)[0] - last = Object.keys(seq2pdb)[Object.keys(seq2pdb).length-1] + var first = Object.keys(seq2pdb)[0]; + var last = Object.keys(seq2pdb)[Object.keys(seq2pdb).length-1]; - rbbt.sequence.clear(sequence_element) - rbbt.svg.clear(svg_element) + rbbt.sequence.clear(sequence_element); + rbbt.svg.clear(svg_element); - rbbt.sequence.mark_position(sequence_element, position) - rbbt.svg.mark_position(svg_element, position) + rbbt.sequence.mark_position(sequence_element, position); + rbbt.svg.mark_position(svg_element, position, 'mark'); if (typeof rbbt.ngl != 'undefined' && seq2pdb[position]) { - rbbt.ngl.ngl_tool('mark_position', position, 'seq') - } else { - alert("Select a PDB") + rbbt.ngl.ngl_viewer('mark_positions_by_colors', 'mark', [position], 'seq', default_colors['mark']); } }) - $('.align.submit').click(function(){ - var link = $(this); - var controls = link.parents('.controls').first(); - var protein_tool = controls.parents('.protein_tool').first(); - var svg_element = protein_tool.find('.svg').first(); - - if(typeof rbbt.ngl != 'undefined'){ - rbbt.svg.mark_aligned_region(svg_element, seq2pdb, 'blue'); - rbbt.ngl.ngl_tool('align') + $('.item.align').click(function(event){ + event.stopPropagation() + var $checkbox = $('.ui.checkbox.align') + if(typeof rbbt.ngl == 'undefined') { + $checkbox.checkbox('set unchecked') + $('.dropdown#marks_pdb').dropdown('hide') + $('.dropdown#marks_pdb').removeClass('left') + return alert("Select a PDB"); + } + $(this).toggleClass('on') + if ($(this).hasClass('on')) { + rbbt.svg.mark_positions(svg_element, Object.keys(seq2pdb).map(Number), 'blue', 'align'); + rbbt.ngl.ngl_viewer('mark_positions_by_colors', 'align', Object.keys(seq2pdb) ,'seq', default_colors['align']); + $checkbox.checkbox('set checked') } else { - alert("Select a PDB") + $checkbox.checkbox('set unchecked') + rbbt.svg.clear(svg_element, 'align') + rbbt.ngl.ngl_viewer('clear_by_layer', 'align'); } - return false; }) $('.mark.neighbours').click(function(){ - if(typeof rbbt.ngl == 'undefined') return alert("Select a PDB") + $('.dropdown#marks_pdb').dropdown('hide') + $('.dropdown#marks_pdb').removeClass('left') + + if(typeof rbbt.ngl == 'undefined') return alert("Select a PDB"); var link = $(this); - console.log('link') - console.log(link) var field = link.parents('.segment').first(); var position = parseInt(field.find('input[name=neighbours]').val()); - if (! position > 0) return alert("No position specified") + if (! position > 0) return alert("No position specified"); var angstroms = parseInt(field.find('input[name=angstroms]').val()); - if (! angstroms > 0) angstroms = 5 + if (! angstroms > 0) angstroms = 5; - - neighbours_cosmic_residue_incidence = {} + var neighbours_cosmic_residue_incidence = {}; rbbt.job("Structure", "neighbour_map", {distance: angstroms, pdb: pdb}, 'true').then(function(all_neighbours_pdb){ - pos_pdb = seq2pdb[position][0] - neighbours_pdb = all_neighbours_pdb[pos_pdb] - neighbours_pdb.push(pos_pdb) - neighbours_seq = neighbours_pdb.map((neighbour_pdb) => pdb2seq[neighbour_pdb]).filter((neighbour_seq) => neighbour_seq).map(String) - pos_colors = rbbt.ngl.ngl_tool('color_mutation_density_subset', neighbours_seq, cosmic_residue_incidence) - rbbt.ngl.ngl_tool('mark_positions_by_colors', pos_colors['pos_list'] ,'seq', pos_colors['colors']) + var pos_pdb = seq2pdb[position][0]; + neighbours_pdb = all_neighbours_pdb[pos_pdb]; + neighbours_pdb.push(pos_pdb); + var neighbours_seq = neighbours_pdb.map(neighbour_pdb => pdb2seq[neighbour_pdb]).filter(neighbour_seq => neighbour_seq).map(String); + var pos_colors = rbbt.ngl.ngl_viewer('color_mutation_density_subset', neighbours_seq, cosmic_residue_incidence); + rbbt.svg.mark_positions(svg_element, pos_colors['pos_list'].map(Number), pos_colors['colors'], 'neighbours'); + rbbt.ngl.ngl_viewer('mark_positions_by_colors', 'neighbours', pos_colors['pos_list'] ,'seq', pos_colors['colors']); }) }) - $('.mark.clusters').click(function(){ - if(typeof rbbt.ngl == 'undefined') return alert("Select a PDB") + $('.mark.clusters').click(function(e){ + $('.dropdown#marks_pdb').dropdown('hide') + + if(typeof rbbt.ngl == 'undefined') return alert("Select a PDB"); var link = $(this); var field = link.parents('.segment').first(); var clusters = parseInt(field.find('input[name=clusters]').val()); - if (clusters < 1 || clusters > 5) return alert("Bad number of clusters") + if (clusters < 1 || clusters > 5) return alert("Bad number of clusters"); if (! clusters > 0) clusters = 3; - if (url.match(/interactome3d/)) pdb = url + if (url.match(/interactome3d/)) pdb = url; - protein = "#{protein}" + var protein = "#{protein}"; rbbt.job("SphereClustering", "protein_cluster", {protein: protein, pdbs: [pdb], spheres: clusters, distance: 5}, 'true').then(function(mutated_clusters){ - keys = Object.keys(mutated_clusters) - keys = keys.map(Number); + var keys = Object.keys(mutated_clusters).map(Number); rbbt.job("Structure", "neighbour_map", {distance: 5, pdb: pdb}, 'true').then(function(all_neighbours_pdb){ for (let cluster of keys) { - pos_pdb = seq2pdb[cluster][0] - neighbours_pdb = all_neighbours_pdb[pos_pdb] - neighbours_pdb.push(pos_pdb) - neighbours_seq = neighbours_pdb.map((neighbour_pdb) => pdb2seq[neighbour_pdb]).filter((neighbour_seq) => neighbour_seq).map(String) - pos_colors = rbbt.ngl.ngl_tool('color_mutation_density_subset', neighbours_seq, cosmic_residue_incidence) - rbbt.ngl.ngl_tool('mark_positions_by_colors', pos_colors['pos_list'] ,'seq', pos_colors['colors']) + var pos_pdb = seq2pdb[cluster][0]; + var neighbours_pdb = all_neighbours_pdb[pos_pdb]; + neighbours_pdb.push(pos_pdb); + var neighbours_seq = neighbours_pdb.map(neighbour_pdb => pdb2seq[neighbour_pdb]).filter(neighbour_seq => neighbour_seq).map(String); + var pos_colors = rbbt.ngl.ngl_viewer('color_mutation_density_subset', neighbours_seq, cosmic_residue_incidence); + rbbt.svg.mark_positions(svg_element, pos_colors['pos_list'].map(Number), pos_colors['colors'], 'clusters'); + rbbt.ngl.ngl_viewer('mark_positions_by_colors', 'clusters', pos_colors['pos_list'] ,'seq', pos_colors['colors']); } - }) - }) - }) - - $('.screenshot').click(function(){ - if(typeof rbbt.ngl == 'undefined') return alert("Select a PDB") - rbbt.ngl.ngl_tool('screenshot') - }) - - $('.resize').click(function(){ - if(typeof rbbt.ngl == 'undefined') return alert("Select a PDB") - rbbt.ngl.ngl_tool('resize') - }) - - $('.spin').click(function(){ - if(typeof rbbt.ngl == 'undefined') return alert("Select a PDB") - spin = $('.ui.button.spin') + }); + }); + }); + + $('#screenshot').click(function(){ + if(typeof rbbt.ngl == 'undefined') return alert("Select a PDB"); + rbbt.ngl.ngl_viewer('screenshot'); + }); + + $('#resize').click(function(){ + if(typeof rbbt.ngl == 'undefined') return alert("Select a PDB"); + rbbt.ngl.ngl_viewer('resize'); + }); + + $('#spin').click(function(){ + if(typeof rbbt.ngl == 'undefined') return alert("Select a PDB"); + var spin = $('#spin'); if (!spin.hasClass('stop')) { - spin.addClass('stop') - spin.html('Stop') + spin.html('Stop'); } else { - spin.removeClass('stop') - spin.html('Spin') + spin.html('Spin'); } - rbbt.ngl.ngl_tool('spin') + spin.toggleClass('stop') + rbbt.ngl.ngl_viewer('spin') }) - $('.fullscreen').click(function(){ - if(typeof rbbt.ngl == 'undefined') return alert("Select a PDB") - rbbt.ngl.ngl_tool('fullscreen') + $('#fullscreen').click(function(){ + if(typeof rbbt.ngl == 'undefined') return alert("Select a PDB"); + rbbt.ngl.ngl_viewer('fullscreen'); }) - }) }) diff --git a/www/views/tools/protein_tool/mutations&features_tabs/Appris_tab.haml b/www/views/tools/protein_tool/mutations&features_tabs/Appris_tab.haml new file mode 100644 index 0000000..61dc712 --- /dev/null +++ b/www/views/tools/protein_tool/mutations&features_tabs/Appris_tab.haml @@ -0,0 +1,33 @@ +%table.ui.celled.table.compact + %thead + %tr + %th + .ui.checkbox.fitted.slider#appris + %input(type='checkbox') + %label + %th Type + %th Start + %th End + %th Description + %tbody + - appris_features.each do |appris_feature| + %tr + %td + .ui.checkbox.fitted.slider.af + %input(type='checkbox') + %label + %td= appris_feature[:type] + %td= appris_feature[:start] + %td= appris_feature[:end] + %td= appris_feature[:description] + +:deferjs + $('.ui.checkbox.slider#appris').click(function(event){ + event.stopPropagation() + $(this).toggleClass('on') + if ($(this).hasClass('on')) { + $('.ui.checkbox.slider.af').checkbox('set checked'); + } else { + $('.ui.checkbox.slider.af').checkbox('set unchecked'); + } + }); diff --git a/www/views/tools/protein_tool/COSMIC_tab.haml b/www/views/tools/protein_tool/mutations&features_tabs/COSMIC_tab.haml similarity index 64% rename from www/views/tools/protein_tool/COSMIC_tab.haml rename to www/views/tools/protein_tool/mutations&features_tabs/COSMIC_tab.haml index 19ab2db..6919acd 100644 --- a/www/views/tools/protein_tool/COSMIC_tab.haml +++ b/www/views/tools/protein_tool/mutations&features_tabs/COSMIC_tab.haml @@ -6,26 +6,27 @@ - colors.each do |c| %option(value=c)= c +.segment(style='overflow-x:scroll') + - header "Genomic Mutation", "GenomicMutation", {:organism => Organism.default_code("Hsa"), :watson => false} + - header "Residue", "NumericValue" -- header "Genomic Mutation", "GenomicMutation", {:organism => Organism.default_code("Hsa"), :watson => false} -- header "Residue", "NumericValue" -- filter "Primary site" -= table :table_id => "COSMIC mutations for #{ protein.name || protein }", :row_ids => :consume do - - log :sample_info + - filter "Primary site" + = table :table_id => "COSMIC mutations for #{ protein.name || protein }", :row_ids => :consume do + - log :sample_info - - sample_info = COSMIC.sample_info.tsv + - sample_info = COSMIC.sample_info.tsv - - sample_info.key_field = "Sample" + - sample_info.key_field = "Sample" - - associations = associations.attach(sample_info) + - associations = associations.attach(sample_info) - - associations.add_field "Residue" do |key,values| - - values["Change"].first.match(/(\d+)/)[0].to_i + - associations.add_field "Residue" do |key,values| + - values["Change"].first.match(/(\d+)/)[0].to_i - - good_fields = ["Genomic Mutation", "Change", "Residue"] + associations.fields - ["Ensembl Protein ID"] + - good_fields = ["Genomic Mutation", "Change", "Residue"] + associations.fields - ["Ensembl Protein ID"] - - log :slice_and_show - - associations.slice(good_fields.uniq) + - log :slice_and_show + - associations.slice(good_fields.uniq) :deferjs @@ -68,11 +69,10 @@ } }) - var protein_tool = $('.protein_tool#' + '#{id}'); rbbt.svg.mark_positions(svg_element, change_positions, color); - if(typeof rbbt.ngl == 'undefined') return alert("Select a PDB") - rbbt.ngl.ngl_tool('color_mutation_density', change_counts) + if(typeof rbbt.ngl == 'undefined') return + rbbt.ngl.ngl_viewer('color_mutation_density', change_counts) } }) return false; diff --git a/www/views/tools/protein_tool/mutations&features_tabs/UniProt_tab.haml b/www/views/tools/protein_tool/mutations&features_tabs/UniProt_tab.haml new file mode 100644 index 0000000..13b241e --- /dev/null +++ b/www/views/tools/protein_tool/mutations&features_tabs/UniProt_tab.haml @@ -0,0 +1,33 @@ +%table.ui.celled.table + %thead + %tr + %th + .ui.checkbox.fitted.slider#uniprot + %input(type='checkbox') + %label + %th Type + %th Start + %th End + %th Description + %tbody + - uniprot_features.each do |uniprot_feature| + %tr + %td + .ui.checkbox.fitted.slider.uf + %input(type='checkbox') + %label + %td= uniprot_feature[:type] + %td= uniprot_feature[:start] + %td= uniprot_feature[:end] + %td= uniprot_feature[:description] + +:deferjs + $('.ui.checkbox.slider#uniprot').click(function(event){ + event.stopPropagation() + $(this).toggleClass('on') + if ($(this).hasClass('on')) { + $('.ui.checkbox.slider.uf').checkbox('set checked'); + } else { + $('.ui.checkbox.slider.uf').checkbox('set unchecked'); + } + });