Commit 0826b274 authored by flothoni's avatar flothoni

Aout en vrac des nombreuses modifications pour utiliser les distribtuion dans le client

Sera rebasé à terme. 
parent 01611d59
......@@ -30,15 +30,18 @@
* @param {object} data - json style object, come directly from .vidjil file
* @param {Model} model
* @param {integer} index - clone index, it's just the clone position in the model's clone array
* @param {boolean} virtual - Set the state of the clone as virtual or not
* @param {boolean} distrib - Set the state of the clone as distrib or not
* */
function Clone(data, model, index, virtual) {
function Clone(data, model, index, virtual, distrib) {
this.m = model
this.index = index
this.split = false
this.seg = {};
this.segEdited = false
this.virtual = typeof virtual !== 'undefined' ? virtual : false
this.distrib = typeof distrib !== 'undefined' ? distrib : false
var key = Object.keys(data)
for (var i=0; i<key.length; i++ ){
......@@ -71,12 +74,14 @@ function Clone(data, model, index, virtual) {
this.m.clusters[index]=[index]
this.m.clones[index]=this
this.tag = this.getTag();
this.computeGCContent()
this.computeCoverage()
this.computeEValue()
if (!this.isDistrib()){
this.computeGCContent()
this.computeCoverage()
this.computeEValue()
// .warn, client computed warnings
this.computeWarnings()
// .warn, client computed warnings
this.computeWarnings()
}
}
function nullIfZero(x) { return (x === 0 || x === '0') ? '' : x }
......@@ -969,7 +974,10 @@ Clone.prototype = {
updateColor: function () {
var allele;
if (this.m.colorMethod == "abundance") {
if (this.isDistrib()){
this.color = colorGeneratorIndex(this.index)
console.log( "set color for a clone distrib")
} else if (this.m.colorMethod == "abundance") {
var size = this.getSize()
if (this.getCluster().length===0){ size = this.getSequenceSize() }
if (size === 0){
......@@ -1462,8 +1470,13 @@ Clone.prototype = {
},
enable: function (top) {
if (this.top > top || this.isVirtual())
if (this.top > top){
return ;
} else if (this.isVirtual() && !this.isDistrib()) {
return
} else if (this.isDistrib() && !this.m.distrib_inclusion) {
return
}
if (this.m.tag[this.getTag()].display) {
this.active = true;
......@@ -1590,14 +1603,53 @@ Clone.prototype = {
return
},
};
/**
* This function return the value of the clone for the list of axis asked.
* @param {Array} axes Array of axis name to get
* @return {array} Array of value for given axis array
*/
get_distributions_values: function(axes){
// LIST_AXES = ["germline", //# "top", # "name"
// "seg5", "seg4", "seg3",
// "lenSeq", // "evalue", l'arrondir ?
// "seg5_delRight", "seg3_delLeft", "seg4_delRight", "seg3_delLeft",
// "insert_53", "insert_54", "insert_43",
// //#"seg5_stop", "seg3_start", "seg4_stop", "seg4_start",
// "lenCDR3", // "cdr3_stop", "cdr3_start",
// "productive", //"junction_start", "junction_stop",
// "rearangment", "complete",
// ]
var values = []
for (var a = 0; a < axes.length; a++) {
var axe = axes[a]
if (axe == "germline") { values.push( this.get('germline') ) }
else if (axe == "seg5") { values.push( this.getGene("5") ) }
else if (axe == "seg4") { values.push( this.getGene("4") ) }
else if (axe == "seg3") { values.push( this.getGene("3") ) }
else if (axe == "lenSeq") { values.push( this.getSequence().length ) }
else if (axe == "seg5_delRight") { values.push( "todo" ) }
else if (axe == "seg3_delLeft") { values.push( "todo" ) }
else if (axe == "seg4_delRight") { values.push( "todo" ) }
else if (axe == "seg3_delLeft") { values.push( "todo" ) }
else if (axe == "insert_53") { values.push( "todo" ) }
else if (axe == "insert_54") { values.push( "todo" ) }
else if (axe == "insert_43") { values.push( "todo" ) }
else if (axe == "lenCDR3") { values.push( this.getSegNtSequence("cdr3").length ) }
else if (axe == "productive") { values.push( this.getProductivityName() ) }
else if (axe == "rearangment") { values.push( "todo" ) }
else if (axe == "complete") { values.push( "todo" ) }
else { values.push( "todo" )}
}
return values
},
isDistrib: function(){
return this.distrib
},
};
......@@ -62,6 +62,7 @@ function Model() {
this.NORM_EXPECTED = "expected"
this.NORM_EXTERNAL = "external"
this.normalization_mode = this.NORM_FALSE
this.distrib_inclusion = true
}
......@@ -121,6 +122,52 @@ Model.prototype = {
}, function() {
$(this).removeClass('hovered');
});
// Table of conversion between axes name and distribution names
this.distrib_convertion = {
"v": "seg5",
"d": "seg4",
"j": "seg3",
"vDel": "seg5_delRight",
"jDel": "seg3_delLeft",
"GCContent" : "GCContent",
"nLength": "insert_53",
"lengthCDR3": "lenCDR3",
"productivity": "productive",
"locus" : "germline",
// Particular, take the nb reads value of the distribution
"size": "size",
// Should be in Array format
"consensusLength" : "lenSeqConsensus",
"averageLength" : "lenSeqAverage",
"coverage": "coverage",
// Don't exist into distribution
"productivity-IMGT": false,
"VIdentity-IMGT": false,
"tag": false,
"sizeOtherSample" : false,
"nbSamples" : false,
"tsneX": false,
"tsneY": false,
"tsneX_system": false,
"tsneY_system": false,
"allele_v": false,
"allele_j": false,
"primers": false,
"occCloneDB": false,
}
// List of axe that must be in an array format
this.distrib_axe_is_timmed = {
"lenSeqConsensus": true,
"lenSeqAverage": true,
"coverage": true,
}
// List of axe that must be returned as number
this.distrib_axe_as_number = {
"GCContent": true
}
},
/**
......@@ -1306,7 +1353,7 @@ changeAlleleNotation: function(alleleNotation) {
other_quantifiable_clones = [];
for (var pos = 0; pos < this.clones.length; pos++) {
var c = this.clone(pos)
if (c.isVirtual()) {
if (c.isVirtual() && !c.isDistrib) {
other_quantifiable_clones.push(pos);
} else if (c.isActive() && c.quantifiable) {
for (var s = 0; s < this.samples.number ; s++) {
......@@ -2837,5 +2884,177 @@ changeAlleleNotation: function(alleleNotation) {
});
},
change_active_distrib: function(axeX, axeY, mode, scatter_id){
console.log( `Change distrib: ${axeX} ${axeY} ${mode}` )
// remove previous distrib clones
this.resetDistribClones(scatter_id)
if (this.distrib_convertion[axeX]==false || this.distrib_convertion[axeY]==false){ return }
// console.log("axe not available");
var axes = [this.distrib_convertion[axeX], this.distrib_convertion[axeY]]
// size is already computed as reads number, so remove it
if (axes.indexOf("size") != -1){
axes.splice(axes.indexOf("size"), 1)
}
// Get the distribution
var distrib = this.getDistrib(axes)
if (distrib != undefined) {
// Create distribution clones
this.createClonesFromDistribution(distrib, scatter_id)
}
},
/**
* For a given distribution, crete distrib clones of it
* @param {Hash} distrib A complete distribution, for a specific timepoint
*/
createClonesFromDistribution: function(distrib, scatter_id){
// recup tous les couple de valeurs possibles
var values = distrib.values
var clone_content = this.get_couple_for_distrib_clones(values, [], [])
console.log( clone_content.toString() )
// clone_content = this.decrease_by_active_clones(clone_content)
// puis en faire une liste de clones
for (var i = 0; i < clone_content.length; i++) {
var content = clone_content[i]
var timepoint = this.samples.order.indexOf(this.t)
var reads = Array.apply(0, Array(this.samples.number))
reads[timepoint] = content[content.length-1][1]
// Compute new index
var index = this.clones.length
// Minimal clone data content
var raw_data = {"reads": [reads], "sequence":0, "id": "distrib_"+index, "top":0, "germline": "distrib", "seg":{}}
// Increase data with specific content
data = this.augmented_distrib_clone_data(raw_data, content, distrib.axes)
new Clone(data, this, index, true, scatter_id)
// console.log( this.clones[index].reads.toString() )
}
},
decrease_by_active_clones: function(clone_content){
for (var i = 0; i < this.clones.length; i++) {
var clone = this.clones[i]
}
},
/**
* Recursive function to return a list of values for the creation of each distrib clones
* The return is an Array of Array. Each element is the array of values for each axes plus the distribution values (aka [clones, reads])
* Compatible with distribution of various axe length
* @param {Array/Hash} values Current values of the given depth
* @param {Array} clones Array (of Array); the list of data to create each distribution clones. Augmented by recursion
* @param {Array} current The current values of data for clones creation
* @return {Array} Return clones values, depending of the depth.
*/
get_couple_for_distrib_clones(values, clones, current){
if (Array.isArray(values)){
current.push(values)
clones.push(current)
return clones
} else {
var keys = Object.keys(values)
for (var pos = 0; pos < keys.length; pos++) {
var key = keys[pos]
var current_array = [...current] // copy
current_array.push(key)
clones = this.get_couple_for_distrib_clones( values[key], clones, current_array)
}
return clones
}
},
/**
* Delete all clones that are isDistrib
*/
resetDistribClones: function(scatter_id){
console.log( `Reset clone distrib: ${scatter_id}`)
console.log( `Before: ${this.clones.length}`)
for (var pos = 0; pos < this.clones.length; pos++) {
var clone = this.clones[pos]
if (clone.isDistrib()){
if (scatter_id== undefined || clone.isDistrib == scatter_id){
this.clones.splice(pos, 1) // verif
}
}
}
console.log( `After: ${this.clones.length}`)
},
/**
* Return the current distribution for the given list of axes
* @param {Array} axes Array of axe name, as available in distributiuon (so must be converted before)
* @return {distribution} The distribution with thze correct axes list and the correct timepoint
*/
getDistrib: function(axes){
var timepoint = this.samples.order.indexOf(this.t)
var timename = this.samples.original_names[timepoint]
var distribs = this.distributions.repertoires[timename]
for (var i = 0; i < distribs.length; i++) {
var distrib = distribs[i]
if (distrib.axes.equals(axes) ){
return distrib
}
}
return undefined
// return this.get_usable_distribution(axes, timepoint)
},
/**
* Increase a default data for distrib clone creation with the correct values at the correct location
* @param {Hash} data Original data to update
* @param {Array} content The values to include into data
* @param {Array} axes List of axes name to set
* @return {Hash} The updated data, compatible for direct creation of distrib clone
*/
augmented_distrib_clone_data: function(data, content, axes){
for (var i = 0; i < axes.length; i++) {
var axe = axes[i]
var value = content[i]
data = this.increase_data(axe, value, data)
}
return data
},
/**
* Increase a data for a given value
* @param {String} axe Axe to set
* @param {Various} value Value to set
* @param {Hash} data Original data to update
* @return {Hash} The data with the new value include
*/
increase_data: function( axe, value, data){
// Convert as number if needed
if (this.distrib_axe_as_number[axe]){ value = Number(value)}
// Convert as a list if needed
if (this.distrib_axe_is_timmed[axe]) {
var timepoint = this.samples.order.indexOf(this.t)
var tmpValue = Array.apply(null, Array(this.samples.number))
tmpValue[timepoint] = value
value = tmpValue
}
// Each content should be hardcoded. Not optimal
if (axe == "GCContent") { data["GCContent"] = value}
if (axe == "consensusLength") { data["consensusLength"] = value}
if (axe == "lenSeqAverage") { data["averageLength"] = value}
if (axe == "lenSeqConsensus") { data["consensusLength"] = value}
if (axe == "seg5") { data["seg"]["5"] = {"name": value} }
if (axe == "seg4") { data["seg"]["4"] = {"name": value} }
if (axe == "seg3") { data["seg"]["3"] = {"name": value} }
// if (axe == "") { data[""] = value}
return data
},
}; //end prototype Model
......@@ -354,7 +354,8 @@ Model_loader.prototype = {
//remove incomplete similarity matrix (TODO: fix fuse.py)
this.similarity = undefined;
this.distributions = data.distributions
// this.init_distributions()
return this
}
......@@ -365,6 +366,123 @@ Model_loader.prototype = {
},
init_distributions: function(){
// Que faire dans cette fonction ?
// Il faut à priori prendre chaque clones et soustraire ses valeurs aux distributions concernés;
// Il faudrait donc à terme pouvoir retrouvé chaque axe d'un clone, comme fait en fuse.py
//
var repnames = Object.keys(this.distributions.repertoires)
console.log( repnames)
// Ne serait-ce pas a faire au moment de l'utilisation de la distribution ?
// Dans ce cas, on ne soustrait que les clones qui sont NON filtrés.
// ainsi, on voit les filtred dans les valeurs des distributions.
// On pourrait aussi imaginer vouloir les voir en séparés.
for (var i = 0; i < repnames.length; i++) {
var repname = repnames[i]
var sample_position = this.samples.original_names.indexOf(repname)
var rep = this.distributions.repertoires[repname]
for (var j = 0; j < rep.length; j++) {
var distrib = rep[j]
for (var c = 0; c < this.clones.length; c++) {
var reads = clone.getReads(sample_position)
// don't remove if clone is not present in this timepoint
if (reads){
var distrib = rep[j]
// pass if clones are unreal, or filtered ?
// How to do that dinamicly... and take into account the filtered ones..., but not all
// Don't show twice the same data.
var clone = this.clones[c]
var values = clone.get_distributions_values(distrib.axes)
var dvalues = distrib.values
// Decrease the size of the distribution for each clones that are not filtered.
// part1, get the position for the current clone
for (var v = 0; v < values.length; v++) {
var axe_val = values[v]
// Warning, if values is see as todo, take it into account for the moment
if (dvalues[axe_val] == undefined){
if (v != values.length-1)
dvalues[axe_val] = {}
else{
dvalues[axe_val] = [0, 0]
}
}
dvalues = dvalues[axe_val]
}
// Soustraction
dvalues[1] -= clone.getReads(sample_position)
dvalues[0] -= 1
// todo; choisir les position d'apres les keys
}
}
var distrib = rep[j]
}
}
},
/**
* Allow to get th e distributions, usable for use for graphical purpose, like genscan like graph.
* @param {[type]} axe [description]
* @param {[type]} sample_position [description]
* @return {[type]} [description]
*/
get_usable_distribution: function(axes, sample_position){
var repname = this.samples.original_names[sample_position]
console.log( repname )
var distribs_rep = this.distributions.repertoires[repname]
for (var j = 0; j < distribs_rep.length; j++) {
var distrib_raw = distribs_rep[j]
if (distrib_raw.axes == axes) { // !!! comment comparer 2 listes ?
for (var c = 0; c < this.clones.length; c++) {
var reads = clone.getReads(sample_position)
// don't remove if clone is not present in this timepoint
if (reads){
var distrib = distrib_raw
// pass if clones are unreal, or filtered ?
// How to do that dinamicly... and take into account the filtered ones..., but not all
// Don't show twice the same data.
var clone = this.clones[c]
if (clone.isVirtual() == false && clone.isActive() == true){
var values = clone.get_distributions_values(distrib.axes)
var dvalues = distrib.values
// Decrease the size of the distribution for each clones that are not filtered.
// part1, get the position for the current clone
for (var v = 0; v < values.length; v++) {
var axe_val = values[v]
// Warning, if values is see as todo, take it into account for the moment
if (dvalues[axe_val] == undefined){
if (v != values.length-1)
dvalues[axe_val] = {}
else{
dvalues[axe_val] = [0, 0]
}
}
dvalues = dvalues[axe_val]
}
// Soustraction
dvalues[1] -= clone.getReads(sample_position)
dvalues[0] -= 1
// todo; choisir les position d'apres les keys
}
}
}
return distrib_raw // values ?
}
}
// distrib don't exist
return []
// Ensuite, il faut faire des clones virtuel qui correspondront à la distribution.
// Dans un premier temps, size/lenSeq
},
getFilteredFields: function(src) {
// Hacky way of managing fields we do not want to copy without restructuring the whole JSON
var exceptions = ['id', 'log', 'producer'];
......
......@@ -1295,6 +1295,8 @@ ScatterPlot.prototype = {
var elem = this.select_x;
this.changeSplitMethod(elem.value, this.splitY, this.mode);
this.cancelPreset()
this.m.change_active_distrib(elem.value, this.splitY, this.mode, this.scatter_id)
this.updateNodes(true)
this.m.update();
},
......@@ -1305,6 +1307,8 @@ ScatterPlot.prototype = {
var elem = this.select_y;
this.changeSplitMethod(this.splitX, elem.value, this.mode);
this.cancelPreset()
this.m.change_active_distrib(this.splitY, elem.value, this.mode, this.scatter_id)
this.updateNodes(true)
this.m.update();
},
......
......@@ -712,4 +712,38 @@ function openAndFillNewTab (content){
html: content
}).appendTo(w.document.body);
return
}
\ No newline at end of file
}
/**
* Function that allow to make comparison between two arrays.
*/
if(Array.prototype.equals)
console.warn("Overriding existing Array.prototype.equals. Possible causes: New API defines the method, there's a framework conflict or you've got double inclusions in your code.");
// attach the .equals method to Array's prototype to call it on any array
Array.prototype.equals = function (array) {
// if the other array is a falsy value, return
if (!array)
return false;
// compare lengths - can save a lot of time
if (this.length != array.length)
return false;
for (var i = 0, l=this.length; i < l; i++) {
// Check if we have nested arrays
if (this[i] instanceof Array && array[i] instanceof Array) {
// recurse into the nested arrays
if (!this[i].equals(array[i]))
return false;
}
else if (this[i] != array[i]) {
// Warning - two different object instances will never be equal: {x:20} != {x:20}
return false;
}
}
return true;
}
// Hide method from for-in loops
Object.defineProperty(Array.prototype, "equals", {enumerable: false});
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment