Commit 5efb64e4 authored by Marc Duez's avatar Marc Duez
Browse files
parents 855e8d0c 8e87d0a1
...@@ -36,5 +36,7 @@ after_success: ...@@ -36,5 +36,7 @@ after_success:
- make should_coveralls - make should_coveralls
notifications: notifications:
email:
- notifications@vidjil.org
webhooks: webhooks:
- https://buildtimetrend.herokuapp.com/travis - https://buildtimetrend.herokuapp.com/travis
#include "windowExtractor.h" #include "windowExtractor.h"
#include "segment.h" #include "segment.h"
// Progress bar
#define PROGRESS_POINT 25000
#define PROGRESS_LINE 40
WindowExtractor::WindowExtractor(): out_segmented(NULL), out_unsegmented(NULL), out_affects(NULL), max_reads_per_window(~0){} WindowExtractor::WindowExtractor(): out_segmented(NULL), out_unsegmented(NULL), out_affects(NULL), max_reads_per_window(~0){}
WindowsStorage *WindowExtractor::extract(OnlineFasta *reads, MultiGermline *multigermline, WindowsStorage *WindowExtractor::extract(OnlineFasta *reads, MultiGermline *multigermline,
...@@ -18,6 +22,7 @@ WindowsStorage *WindowExtractor::extract(OnlineFasta *reads, MultiGermline *mult ...@@ -18,6 +22,7 @@ WindowsStorage *WindowExtractor::extract(OnlineFasta *reads, MultiGermline *mult
} }
int nb_reads_all = 0; int nb_reads_all = 0;
int bp_total = 0;
while (reads->hasNext() && (int) nb_reads != stop_after) { while (reads->hasNext() && (int) nb_reads != stop_after) {
reads->next(); reads->next();
...@@ -74,7 +79,23 @@ WindowsStorage *WindowExtractor::extract(OnlineFasta *reads, MultiGermline *mult ...@@ -74,7 +79,23 @@ WindowsStorage *WindowExtractor::extract(OnlineFasta *reads, MultiGermline *mult
if (out_affects) { if (out_affects) {
*out_affects << "#" << seg->getInfoLine() << endl; *out_affects << "#" << seg->getInfoLine() << endl;
} }
// Progress bar
bp_total += read_length;
if (!(nb_reads % PROGRESS_POINT))
{
cout << "." ;
if (!(nb_reads % (PROGRESS_POINT * PROGRESS_LINE)))
cout << setw(10) << nb_reads / 1000 << "k reads " << fixed << setprecision(2) << setw(14) << bp_total / 1E6 << " Mbp" << endl ;
cout.flush() ;
}
} }
cout << endl ;
return windowsStorage; return windowsStorage;
} }
......
...@@ -180,6 +180,12 @@ line { ...@@ -180,6 +180,12 @@ line {
opacity: 0.5 ; opacity: 0.5 ;
stroke-width: 20px; stroke-width: 20px;
} }
.axis_m_other {
stroke: #839496;
opacity: 0.25 ;
stroke-width: 10px;
display: none;
}
.axis_h { .axis_h {
stroke: #839496; stroke: #839496;
} }
......
...@@ -180,6 +180,12 @@ line { ...@@ -180,6 +180,12 @@ line {
opacity: 0.5 ; opacity: 0.5 ;
stroke-width: 20px; stroke-width: 20px;
} }
.axis_m_other {
stroke: #657b83;
opacity: 0.25 ;
stroke-width: 10px;
display: none;
}
.axis_h { .axis_h {
stroke: #657b83; stroke: #657b83;
} }
......
...@@ -228,6 +228,13 @@ line { ...@@ -228,6 +228,13 @@ line {
stroke-width : 20px; stroke-width : 20px;
} }
.axis_m_other {
stroke: @default ;
opacity: 0.25 ;
stroke-width: 10px;
display: none;
}
.axis_h{ .axis_h{
stroke : @default ; stroke : @default ;
} }
......
...@@ -117,7 +117,7 @@ Axis.prototype = { ...@@ -117,7 +117,7 @@ Axis.prototype = {
/* use clone size /* use clone size
* *
* */ * */
useSize: function () { useSize: function (other) {
this.init() this.init()
var self = this; var self = this;
this.sizeScale = d3.scale.log() this.sizeScale = d3.scale.log()
...@@ -126,7 +126,8 @@ Axis.prototype = { ...@@ -126,7 +126,8 @@ Axis.prototype = {
//clone position //clone position
this.pos = function(cloneID) { this.pos = function(cloneID) {
var size = self.m.clone(cloneID).getSize() var time = other ? self.m.tOther : self.m.t
var size = self.m.clone(cloneID).getSize(time)
if (size !=0 ) { if (size !=0 ) {
return self.sizeScale(size); return self.sizeScale(size);
}else{ }else{
......
...@@ -102,6 +102,7 @@ function Graph(id, model) { ...@@ -102,6 +102,7 @@ function Graph(id, model) {
this.text_position_x = 60; this.text_position_x = 60;
this.m.view.push(this) this.m.view.push(this)
this.m.graph = this // TODO: find a better way to do this
} }
Graph.prototype = { Graph.prototype = {
...@@ -1066,10 +1067,28 @@ Graph.prototype = { ...@@ -1066,10 +1067,28 @@ Graph.prototype = {
this.mobil.pos = this.graph_col[this.m.samples.order.indexOf(this.m.t)]; this.mobil.pos = this.graph_col[this.m.samples.order.indexOf(this.m.t)];
this.data_axis.push(this.mobil) this.data_axis.push(this.mobil)
} }
//other selected time_point
if ( this.m.samples.order.indexOf(this.m.tOther) != -1 ){
this.mobilOther = {};
this.mobilOther.type = "axis_m_other";
this.mobilOther.text = "";
this.mobilOther['class'] = "graph_text";
this.mobilOther.orientation = "vert";
this.mobilOther.pos = this.graph_col[this.m.samples.order.indexOf(this.m.tOther)];
this.data_axis.push(this.mobilOther)
}
return this return this
}, },
setOtherVisibility: function (visible) {
if (visible) {
$(".axis_m_other").show("fast")
} else {
$(".axis_m_other").hide("fast")
}
},
/* ************************************************ * /* ************************************************ *
* RENDERER FUNCTIONS * RENDERER FUNCTIONS
......
...@@ -72,7 +72,8 @@ Model.prototype = { ...@@ -72,7 +72,8 @@ Model.prototype = {
this.data = {}; this.data = {};
this.data_info = {}; this.data_info = {};
this.t = 0; this.t = 0; // Selected time/sample
this.tOther = 0; // Other (previously) selected time/sample
this.focus = -1; this.focus = -1;
this.system_selected = [] this.system_selected = []
...@@ -389,6 +390,10 @@ Model.prototype = { ...@@ -389,6 +390,10 @@ Model.prototype = {
self.samples.order = [] self.samples.order = []
for (var i = 0; i < self.samples.number; i++) self.samples.order.push(i); for (var i = 0; i < self.samples.number; i++) self.samples.order.push(i);
} }
if (self.samples.order.length >= 2) {
self.tOther = 1
}
if (typeof self.samples.names =='undefined'){ if (typeof self.samples.names =='undefined'){
self.samples.names = [] self.samples.names = []
for (var i = 0; i < self.samples.number; i++) self.samples.names.push(""); for (var i = 0; i < self.samples.number; i++) self.samples.names.push("");
...@@ -1479,6 +1484,7 @@ Model.prototype = { ...@@ -1479,6 +1484,7 @@ Model.prototype = {
* */ * */
changeTime: function (newT) { changeTime: function (newT) {
console.log("changeTime()" + newT) console.log("changeTime()" + newT)
this.tOther = this.t
this.t = newT; this.t = newT;
this.update(); this.update();
return this.t return this.t
......
...@@ -103,7 +103,8 @@ function ScatterPlot(id, model) { ...@@ -103,7 +103,8 @@ function ScatterPlot(id, model) {
["gene_j", "gene J"], ["gene_j", "gene J"],
["allele_v", "allele V"], ["allele_v", "allele V"],
["allele_j", "allele J"], ["allele_j", "allele J"],
["Size", "abundance"], ["Size", "size"],
["otherSize", "size (other point)"],
["sequenceLength", "clone length"], ["sequenceLength", "clone length"],
["GCContent", "GC content"], ["GCContent", "GC content"],
["n", "N length"], ["n", "N length"],
...@@ -119,7 +120,8 @@ function ScatterPlot(id, model) { ...@@ -119,7 +120,8 @@ function ScatterPlot(id, model) {
// "V/abundance" : { "mode": "plot", "x" : "gene_v", "y": "Size"}, // "V/abundance" : { "mode": "plot", "x" : "gene_v", "y": "Size"},
"V distribution" : { "mode": "bar", "x" : "gene_v", "y": "gene_j"}, "V distribution" : { "mode": "bar", "x" : "gene_v", "y": "gene_j"},
"Clone length distribution" : { "mode": "bar", "x" : "sequenceLength", "y": "gene_v"}, "Clone length distribution" : { "mode": "bar", "x" : "sequenceLength", "y": "gene_v"},
"N length distribution" : { "mode": "bar", "x" : "n", "y": "gene_v"} "N length distribution" : { "mode": "bar", "x" : "n", "y": "gene_v"},
"compare two samples" : { "mode": "plot", "x" : "Size", "y": "otherSize"}
}; };
this.default_preset = 1 this.default_preset = 1
...@@ -846,6 +848,9 @@ ScatterPlot.prototype = { ...@@ -846,6 +848,9 @@ ScatterPlot.prototype = {
case "gene_j" : case "gene_j" :
this.sortBarTab(function(cloneID){return self.m.clone(cloneID).getJ(false)}); this.sortBarTab(function(cloneID){return self.m.clone(cloneID).getJ(false)});
break; break;
case "otherSize" :
this.sortBarTab(function(cloneID){return self.m.clone(cloneID).getSize(m.tOther)});
break;
case "Size" : case "Size" :
this.sortBarTab(function(cloneID){return self.m.clone(cloneID).getSize()}); this.sortBarTab(function(cloneID){return self.m.clone(cloneID).getSize()});
break; break;
...@@ -1959,6 +1964,11 @@ ScatterPlot.prototype = { ...@@ -1959,6 +1964,11 @@ ScatterPlot.prototype = {
}else{ }else{
this.update(); this.update();
} }
if (typeof m.graph != "undefined") {
m.graph.setOtherVisibility(this.splitX == "otherSize" || this.splitY == "otherSize")
}
}, },
...@@ -1976,8 +1986,11 @@ ScatterPlot.prototype = { ...@@ -1976,8 +1986,11 @@ ScatterPlot.prototype = {
case "gene_j" : case "gene_j" :
axis.useGermline(this.m.germlineJ, "J", false) axis.useGermline(this.m.germlineJ, "J", false)
break; break;
case "otherSize" :
axis.useSize(true)
break;
case "Size" : case "Size" :
axis.useSize() axis.useSize(false)
break; break;
case "sequenceLength" : case "sequenceLength" :
axis.custom(function(cloneID) { axis.custom(function(cloneID) {
......
...@@ -50,6 +50,8 @@ function Segment(id, model) { ...@@ -50,6 +50,8 @@ function Segment(id, model) {
this.m.view.push(this); //synchronisation au Model this.m.view.push(this); //synchronisation au Model
this.starPath = "M 0,6.1176482 5.5244193, 5.5368104 8.0000008,0 10.172535,5.5368104 16,6.1176482 11.406183,9.9581144 12.947371,16 8.0000008,12.689863 3.0526285,16 4.4675491,10.033876 z" this.starPath = "M 0,6.1176482 5.5244193, 5.5368104 8.0000008,0 10.172535,5.5368104 16,6.1176482 11.406183,9.9581144 12.947371,16 8.0000008,12.689863 3.0526285,16 4.4675491,10.033876 z"
this.cgi_address = CGI_ADDRESS this.cgi_address = CGI_ADDRESS
this.first_clone = 0 ; // id of sequence at the top of the segmenter
this.memtab = []; this.memtab = [];
this.sequence = {}; this.sequence = {};
...@@ -447,6 +449,13 @@ Segment.prototype = { ...@@ -447,6 +449,13 @@ Segment.prototype = {
this.sequence[cloneID] = new Sequence(cloneID, this.m) this.sequence[cloneID] = new Sequence(cloneID, this.m)
var divParent = document.getElementById("listSeq"); var divParent = document.getElementById("listSeq");
// Am I the first clone in this segmenter ?
var previous_li = document.getElementById("listSeq").getElementsByTagName("li");
if (previous_li.length == 0) {
this.first_clone = cloneID
}
var li = document.createElement('li'); var li = document.createElement('li');
li.id = "seq" + cloneID; li.id = "seq" + cloneID;
li.className = "sequence-line"; li.className = "sequence-line";
...@@ -542,8 +551,8 @@ Segment.prototype = { ...@@ -542,8 +551,8 @@ Segment.prototype = {
$(".seq-mobil").css({ opacity: 1 }) $(".seq-mobil").css({ opacity: 1 })
}, },
success: function (result) { success: function (result) {
self.displayAjaxResult(result);
self.aligned = true ; self.aligned = true ;
self.displayAjaxResult(result);
}, },
error: function () { error: function () {
console.log({"type": "flash", "msg": "cgi error : impossible to connect", "priority": 2}); console.log({"type": "flash", "msg": "cgi error : impossible to connect", "priority": 2});
...@@ -582,29 +591,30 @@ Segment.prototype = { ...@@ -582,29 +591,30 @@ Segment.prototype = {
resetAlign: function() { resetAlign: function() {
var selected = this.m.getSelected(); var selected = this.m.getSelected();
this.aligned = false
for (var i = 0; i < selected.length; i++) { for (var i = 0; i < selected.length; i++) {
var spanM = document.getElementById("m" + selected[i]) var spanM = document.getElementById("m" + selected[i])
spanM.innerHTML = this.sequence[selected[i]].load().toString() spanM.innerHTML = this.sequence[selected[i]].load().toString()
} }
this.aligned = false
}, },
displayAjaxResult: function(file) { displayAjaxResult: function(file) {
var json = JSON.parse(file) var json = JSON.parse(file)
// Load all (aligned) sequences
for (var i = 0; i < json.seq.length; i++) {
this.sequence[this.memTab[i]].load(json.seq[i])
}
// Render all (aligned) sequences
for (var i = 0; i < json.seq.length; i++) { for (var i = 0; i < json.seq.length; i++) {
// global container // global container
var spanM = document.getElementById("m" + this.memTab[i]); var spanM = document.getElementById("m" + this.memTab[i]);
spanM.innerHTML = "";
var seq = this.sequence[this.memTab[i]] var seq = this.sequence[this.memTab[i]]
spanM.innerHTML = seq.load(json.seq[i]) spanM.innerHTML = seq.toString()
.diff(json.seq[0])
.toString()
} }
}, },
...@@ -805,13 +815,13 @@ Sequence.prototype = { ...@@ -805,13 +815,13 @@ Sequence.prototype = {
}, },
//compare sequence with another string and surround change //compare sequence with another string and surround change
diff: function (str) { spanify_mutation: function (self, other) {
for (var i = 0; i < this.seq.length; i++) { if (segment.aligned && self != other) {
if (this.seq[i] != str[i]) { return "<span class='substitution' other='" + other + '-' + segment.first_clone + "'>" + self + "</span>"
this.seq[i] = "<span class='substitution'>" + this.seq[i] + "</span>"
}
} }
return this; else {
return self
}
}, },
//return sequence completed with html tag //return sequence completed with html tag
...@@ -876,9 +886,9 @@ Sequence.prototype = { ...@@ -876,9 +886,9 @@ Sequence.prototype = {
// one character // one character
if (segment.amino) { if (segment.amino) {
result += this.seqAA[i] result += this.spanify_mutation(this.seqAA[i], segment.sequence[segment.first_clone].seqAA[i])
}else{ }else{
result += this.seq[i] result += this.spanify_mutation(this.seq[i], segment.sequence[segment.first_clone].seq[i])
} }
// VDJ spans - end // VDJ spans - end
......
#+TITLE: Vidjil Algorithm -- Browser development documentation #+TITLE: Vidjil Browser -- development documentation
#+AUTHOR: The Vidjil team (Marc, Mathieu, Mikaël and Tatiana) #+AUTHOR: The Vidjil team (Marc, Mathieu, Mikaël and Tatiana)
...@@ -21,7 +21,7 @@ as well with the locus selection. ...@@ -21,7 +21,7 @@ as well with the locus selection.
- The link with the patient database/server is done with the =Database= object (=js/database.js=) - The link with the patient database/server is done with the =Database= object (=js/database.js=)
- Other objects: =Report=, =Shortcut= - Other objects: =Report=, =Shortcut=
Extends functionalities but cannot be currently used outside the default vidjil browser. Extends functionalities but requires elements from the full =index.html=.
** Integrating the browser ** Integrating the browser
...@@ -37,10 +37,30 @@ as well with the locus selection. ...@@ -37,10 +37,30 @@ as well with the locus selection.
- The wonderful library =require.js= is used, so there is only one file to include - The wonderful library =require.js= is used, so there is only one file to include
<script data-main="js/app.js" src="js/lib/require.js"></script> <script data-main="js/app.js" src="js/lib/require.js"></script>
- =js/main.js= is the main file that creates the different views and binds them to the model. - =js/main.js= creates the different views and binds them to the model.
Or you can define a function named "main()" and use it to build your own interface (see =small_example.html=) Another option is to directly define a function named =main()=, as in =small_example.html=.
*** JSON .vidjil data
Clone lists can be passed to the model through several ways:
- directly by the user (import/export)
- from a patient database (needs a database)
- trough the API (see below)
- or by directly providing data through Javascript (as in =small_example.html=)
The first three solutions needs some further elements from the full =index.html=.
**** Browser API
The browser can be opened on a data file specified from a =data= attribute,
and optionally on an analysis file specified from a =analysis= attribute,
as in the following URLs on our test server:
- http://rbx.vidjil.org/browser/?data=test.vidjil
- http://rbx.vidjil.org/browser/?data=test.vidjil&analysis=test.analysis
- http://rbx.vidjil.org/browser/?data=http://rbx.vidjil.org/browser/test.vidjil
Both GET and POST requests are accepted.
Note that the =browser/index.html= file and the =.vidjil/.analysis= files should be hosted on the same server.
Otherwise, the server hosting the =.vidjil/.analysis= files must accept cross-domain queries.
...@@ -319,20 +319,6 @@ There can be several causes leading to bad ratios: ...@@ -319,20 +319,6 @@ There can be several causes leading to bad ratios:
* Browser API
The browser can be opened on a data file specified from a =data= attribute,
and optionally on an analysis file specified from a =analysis= attribute,
as in the following URLs on our test server:
- http://rbx.vidjil.org/browser/?data=test.vidjil
- http://rbx.vidjil.org/browser/?data=test.vidjil&analysis=test.analysis
- http://rbx.vidjil.org/browser/?data=http://rbx.vidjil.org/browser/test.vidjil
Both GET and POST requests are accepted.
Note that the =browser/index.html= file and the =.vidjil/.analysis= files should be hosted on the same server.
Otherwise, the server hosting the =.vidjil/.analysis= files must accept cross-domain queries.
* Reference * Reference
......
def parse(fasta, endline=''):
'''Iterates over sequences in a fasta files, yielding (header, sequence) pairs'''
header = ''
sequence = ''
for l in fasta:
l = l.strip()
if not l:
continue
if l[0] == '>':
if header or sequence:
yield (header, sequence)
header = l[1:]
sequence = ''
else:
sequence += l + endline
if header or sequence:
yield (header, sequence)
def parse_as_Fasta(fasta):
for (header, sequence) in parse(fasta):
yield Fasta(header, sequence)
class Fasta():
def __init__(self, header, sequence):
self.header = header
self.seq = sequence
@property
def name(self):
return self.header.split('|')[1]
@property
def species(self):
return self.header.split('|')[2]
def __len__(self):
return len(self.seq)