Commit e1d8b361 authored by Mathieu Giraud's avatar Mathieu Giraud

Merge branch 'feature-c/878-stockage-des-preferences' into 'dev'

Feature c/878 stockage des preferences

See merge request !785
parents 8270d149 b016743a
Pipeline #169448 passed with stages
in 8 minutes and 52 seconds
......@@ -203,29 +203,55 @@
</div>
<div class="menu_box">
size display</br>
<label for="menuNotationScientific" class="buttonSelector" onclick="m.changeNotation('scientific', true)"><input id="menuNotationScientific" type="radio" name="notation" value="scientific" />scientific notation</label>
<label for="menuNotationPercent" class="buttonSelector" onclick="m.changeNotation('percent', true)"><input id="menuNotationPercent" type="radio" name="notation" value="percent" checked />percent</label>
<label for="menuNotation_scientific" class="buttonSelector" onclick="m.changeNotation('scientific', true, true)">
<input id="menuNotation_scientific" type="radio" name="notation" value="scientific" />scientific notation
</label>
<label for="menuNotation_percent" class="buttonSelector" onclick="m.changeNotation('percent', true, true)">
<input id="menuNotation_percent" type="radio" name="notation" value="percent" checked />percent
</label>
</div>
<div class="menu_box">
sample key</br>
<label for="menuTimeFormName" class="buttonSelector" onclick="m.changeTimeFormat('name', true)"><input id="menuTimeFormName" type="radio" name="time" value="name" checked />name</label>
<label for="menuTimeFormShort" class="buttonSelector" onclick="m.changeTimeFormat('short_name', true)"><input id="menuTimeFormShort" type="radio" name="time" value="short_name"/>name (short)</label>
<label for="menuTimeFormSample" class="buttonSelector" onclick="m.changeTimeFormat('sampling_date', true)"><input id="menuTimeFormSample" type="radio" name="time" value="sampling_date" />sampling date</label>
<label for="menuTimeFormSamplePlus" class="buttonSelector" onclick="m.changeTimeFormat('delta_date', true)"><input id="menuTimeFormSamplePlus" type="radio" name="time" value="delta_date" />day after first sample</label>
<label for="menuTimeForm_name" class="buttonSelector" onclick="m.changeTimeFormat('name', true, true)">
<input id="menuTimeForm_name" type="radio" name="time" value="name" checked />name
</label>
<label for="menuTimeForm_short_name" class="buttonSelector" onclick="m.changeTimeFormat('short_name', true, true)">
<input id="menuTimeForm_short_name" type="radio" name="time" value="short_name" />name (short)
</label>
<label for="menuTimeForm_sampling_date" class="buttonSelector" onclick="m.changeTimeFormat('sampling_date', true, true)">
<input id="menuTimeForm_sampling_date" type="radio" name="time" value="sampling_date" />sampling date
</label>
<label for="menuTimeForm_delta_date" class="buttonSelector" onclick="m.changeTimeFormat('delta_date', true, true)">
<input id="menuTimeForm_delta_date" type="radio" name="time" value="delta_date" />day after first sample
</label>
</div>
<div class="menu_box">
N regions in clone names</br>
<label for="menuCloneNotNucNum" class="buttonSelector" onclick="m.changeCloneNotation('nucleotide_number', true)"><input id="menuCloneNotNucNum" type="radio" name="show_name" value="nucleotide_number" />length</label>
<label for="menuCloneNotSeqShort" class="buttonSelector" onclick="m.changeCloneNotation('short_sequence', true)"><input id="menuCloneNotSeqShort" type="radio" name="show_name" value="short_sequence" checked />sequence (when short)</label>
<label for="menuCloneNotSeq" class="buttonSelector" onclick="m.changeCloneNotation('full_sequence', true)"><input id="menuCloneNotSeq" type="radio" name="show_name" value="full_sequence" />sequence (always)</label>
<label for="menuCloneNot_nucleotide_number" class="buttonSelector" onclick="m.changeCloneNotation('nucleotide_number', true, true)">
<input id="menuCloneNot_nucleotide_number" type="radio" name="show_name" value="nucleotide_number" />length
</label>
<label for="menuCloneNot_short_sequence" class="buttonSelector" onclick="m.changeCloneNotation('short_sequence', true, true)">
<input id="menuCloneNot_short_sequence" type="radio" name="show_name" value="short_sequence" checked />sequence (when short)
</label>
<label for="menuCloneNot_full_sequence" class="buttonSelector" onclick="m.changeCloneNotation('full_sequence', true, true)">
<input id="menuCloneNot_full_sequence" type="radio" name="show_name" value="full_sequence" />sequence (always)
</label>
</div>
<div class="menu_box">
alleles in clone names</br>
<label for="menu_allele_never" class="buttonSelector" onclick="m.changeAlleleNotation('never', true)"><input id="menu_allele_never" type="radio" name="menu_allele" value="never" />never</label>
<label for="menu_allele_when_not_01" class="buttonSelector" onclick="m.changeAlleleNotation('when_not_01', true)"><input id="menu_allele_when_not_01" type="radio" name="menu_allele" value="when_not_01" checked />when not *01</label>
<label for="menu_allele_always" class="buttonSelector" onclick="m.changeAlleleNotation('always', true)"><input id="menu_allele_always" type="radio" name="menu_allele" value="always" />always</label>
<label for="menuAlleleNot_never" class="buttonSelector" onclick="m.changeAlleleNotation('never', true, true)">
<input id="menuAlleleNot_never" type="radio" name="menu_allele" value="never" />never
</label>
<label for="menuAlleleNot_when_not_01" class="buttonSelector" onclick="m.changeAlleleNotation('when_not_01', true, true)">
<input id="menuAlleleNot_when_not_01" type="radio" name="menu_allele" value="when_not_01" checked />when not *01
</label>
<label for="menuAlleleNot_always" class="buttonSelector" onclick="m.changeAlleleNotation('always', true, true)">
<input id="menuAlleleNot_always" type="radio" name="menu_allele" value="always" />always
</label>
</div>
<div class="menu_box">
<a class="buttonSelector" onclick="m.resetSettings();">reset settings</a>
</div>
<div class="menu_box devel-mode">
......
......@@ -53,6 +53,8 @@ function Model() {
this.germline = {};
this.create_germline_obj();
this.view = [];
this.checkLocalStorage();
this.reset();
this.setAll();
this.checkBrowser();
this.germlineList = new GermlineList()
......@@ -174,13 +176,22 @@ Model.prototype = {
* Set all the properties. Called in the constructor.
*/
setAll: function () {
this.reset();
this.system_selected = []
this.colorMethod = "Tag";
this.changeNotation("percent", false)
this.changeTimeFormat("name", false)
this.top = 50;
this.top = 50
if (this.localStorage){
if (localStorage.getItem('colorMethod')) this.colorMethod = localStorage.getItem('colorMethod')
if (localStorage.getItem('timeFormat')) this.time_type = localStorage.getItem('timeFormat')
if (localStorage.getItem('notation')) this.notation_type = localStorage.getItem('notation')
if (localStorage.getItem('alleleNotation')) this.alleleNotation = localStorage.getItem('alleleNotation')
if (localStorage.getItem('cloneNotation')) this.cloneNotationType = localStorage.getItem('cloneNotation')
}
this.changeColorMethod(this.colorMethod, false)
this.changeNotation(this.notation_type, false)
this.changeTimeFormat(this.time_type, false)
this.changeAlleleNotation(this.alleleNotation, false)
this.changeCloneNotation(this.cloneNotationType, false)
},
/**
* remove all elements from the previous .vidjil file but keep current user parameters and linked views
......@@ -205,6 +216,9 @@ Model.prototype = {
this.tOther = 0; // Other (previously) selected time/sample
this.focus = -1;
this.colorMethod = "Tag"
this.notation_type = "percent"
this.time_type = "name"
this.display_window = false
this.isPlaying = false;
......@@ -327,12 +341,12 @@ Model.prototype = {
$("#expected_normalization").hide();
// time_type to name_short if there is many samples
if (this.samples.order.length > 6)
if (this.samples.order.length > 6 && !localStorage.getItem("timeFormat"))
this.changeTimeFormat("short_name", false)
// time_type to delta_date if we have enough different dates
deltas = this.dateDiffMinMax()
if (deltas.max > 1)
if (deltas.max > 1 && !localStorage.getItem("timeFormat"))
this.changeTimeFormat("delta_date", false)
// NSIZE
......@@ -368,15 +382,29 @@ Model.prototype = {
}
}, //end initClones
changeCloneNotation: function(cloneNotationType) {
this.cloneNotationType = cloneNotationType;
this.update();
changeCloneNotation: function(cloneNotationType, update, save) {
this.cloneNotationType = cloneNotationType
if (this.localStorage && save) localStorage.setItem('cloneNotation', cloneNotationType)
var menu = document.getElementById("menuCloneNot_" + cloneNotationType)
if (menu) menu.checked = true
update = (update==undefined) ? true : update
if (update) this.update();
},
changeAlleleNotation: function(alleleNotation) {
changeAlleleNotation: function(alleleNotation, update, save) {
this.alleleNotation = alleleNotation;
this.update();
if (this.localStorage && save) localStorage.setItem('alleleNotation', alleleNotation)
var menu = document.getElementById("menuAlleleNot_" + alleleNotation)
if (menu) menu.checked = true
update = (update==undefined) ? true : update
if (update) this.update();
},
/**
......@@ -2183,6 +2211,26 @@ changeAlleleNotation: function(alleleNotation) {
},
/**
* check browser local storage availability
* */
checkLocalStorage: function () {
try {
this.localStorage = window.localStorage
var x = '__storage_test__'
this.localStorage.setItem(x, x)
this.localStorage.removeItem(x)
}
catch(e) {
return e instanceof DOMException && (
e.code === 22 ||
e.code === 1014 ||
e.name === 'QuotaExceededError' ||
e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
(this.localStorage && this.localStorage.length !== 0)
}
},
NB_READS_THRESHOLD_QUANTIFIABLE: 5,
......@@ -2450,32 +2498,63 @@ changeAlleleNotation: function(alleleNotation) {
/**
* change default notation display for sizes
* @param {string} notation - notation type ('scientific' , 'percent')
* @pram {bool} update - will update the display after
* @param {bool} update - will update the display after
* @param {bool} save - will save value in user preferences (localStorage)
* */
changeNotation: function (notation, update) {
changeNotation: function (notation, update, save) {
this.notation_type = notation
if (this.localStorage && save) localStorage.setItem('notation', notation)
var menu = document.getElementById("menuNotation_" + notation)
if (menu) menu.checked = true
if (update) this.update()
},
/**
* change default time format for sample/time names
* @param {string} notation - format ('name', 'sampling_date', 'delta_date', 'delta_date_no_zero')
* @pram {bool} update - will update the display after
* @param {bool} update - will update the display after
* @param {bool} save - will save value in user preferences (localStorage)
* */
changeTimeFormat: function (time, update) {
changeTimeFormat: function (time, update, save) {
this.time_type = time
if (this.localStorage && save) localStorage.setItem('timeFormat', time)
var menu = document.getElementById("menuTimeForm_" + time)
if (menu) menu.checked = true
if (update) this.update()
},
/**
* change default color method
* @param {string} colorM - TODO
* @param {string} colorM
* @param {bool} update - will update the display after default = true
* @param {bool} save - will save value in user preferences (localStorage)
* */
changeColorMethod: function (colorM) {
this.colorMethod = colorM;
var list = [];
for (var i = 0; i<this.clones.length; i++) list.push(i);
this.updateElemStyle(list);
changeColorMethod: function (colorM, update, save) {
update = (update==undefined) ? true : update;
this.colorMethod = colorM
if (this.localStorage && save) localStorage.setItem('colorMethod', colorM)
var menu = document.getElementById("color_menu_select")
if (menu) menu.value = colorM
if (!update) return
var list = []
for (var i = 0; i<this.clones.length; i++) list.push(i)
this.updateElemStyle(list)
},
resetSettings: function () {
localStorage.clear()
this.changeColorMethod("Tag", false)
this.changeNotation("percent", false)
this.changeTimeFormat("name", false)
this.changeAlleleNotation("when_not_01", false)
this.changeCloneNotation("short_sequence", false)
console.log({ msg: "user preferences have been reset", type: "flash", priority: 1 });
},
/**
......
......@@ -107,6 +107,7 @@ Model_loader.prototype = {
oFReader.readAsText(oFile);
oFReader.onload = function (oFREvent) {
self.reset();
self.setAll()
self.parseJsonData(oFREvent.target.result, limit);
self.loadGermline()
.initClones()
......@@ -193,6 +194,7 @@ Model_loader.prototype = {
url: url,
success: function (result) {
self.reset();
self.setAll();
self.parseJsonData(result, 100)
.loadGermline()
.initClones()
......@@ -269,6 +271,7 @@ Model_loader.prototype = {
return 0;
}
self.reset();
self.setAll()
//copy .vidjil file in model
var store_config = this.config;
......
......@@ -272,25 +272,20 @@ Segment.prototype = {
span.id = 'highlightCheckboxes';
if(this.findPotentialField().indexOf('cdr3') != -1) {
var input = document.createElement('input');
input = document.createElement('input');
input.type = 'checkbox';
input.id = 'vdj_input_check';
$(input).on("click", function() {
if(this.checked) {
self.highlight[0].field = "cdr3";
self.highlight[0].color = "red";
} else {
self.highlight[0].field = "";
}
self.update();
input.setAttribute("title", 'Display CDR3 computed by Vidjil');
input.autocomplete = 'off';
input.checked = (self.m.localStorage && localStorage.getItem('segmenter_cdr3')=="checked")
input.addEventListener('change', function(evt){
self.highlightCDR3(event.target.checked, true)
})
self.checkboxCDR3 = input
});
var label = document.createElement('label');
label.setAttribute("for", 'vdj_input_check');
label.innerHTML = 'CDR3';
input.setAttribute("title", 'Display CDR3 computed by Vidjil');
label.setAttribute("title", 'Display CDR3 computed by Vidjil');
span.appendChild(input);
......@@ -420,8 +415,10 @@ Segment.prototype = {
for (var c_id = 0; c_id < self.m.clones.length; c_id++)
self.build_skeleton(c_id);
if (self.highlightCDR3)
this.highlightCDR3(self.highlightCDR3.checked, false)
} catch(err) {
sendErrorToDb(err, this.db);
}
......@@ -1289,6 +1286,30 @@ Segment.prototype = {
empty: function() {
this.reset();
},
/** set the checkbox for highlight cdr3
* @param {bool} - checkbox value
* @param {bool} - save in localStorage as user preference
*/
highlightCDR3: function(checked, save){
//if value is unspecified, retrieve current value of the checkbox to sync segmenter internal parameter with it
if (typeof checked == 'undefined')
if (this.checkboxCDR3)
checked = this.checkboxCDR3.checked
if (checked) {
this.highlight[0].field = "cdr3"
this.highlight[0].color = "red"
if (this.m.localStorage && save) localStorage.setItem('segmenter_cdr3', "checked")
if (this.checkboxCDR3) this.checkboxCDR3.checked = true
} else {
this.highlight[0].field = ""
if (this.m.localStorage && save) localStorage.setItem('segmenter_cdr3', "unchecked")
if (this.checkboxCDR3) this.checkboxCDR3.checked = false
}
this.update()
}
}; //fin prototype Segment
......
......@@ -26,7 +26,7 @@ class BrowserTest < MiniTest::Test
end
end
def set_browser(vidjil_file, analysis_file=nil)
def set_browser(vidjil_file, analysis_file=nil, local_storage=nil)
folder_path = File.expand_path(File.dirname(__FILE__))
folder_path.sub! '/browser/test/functional', ''
index_path = 'file://' + folder_path + '/browser/index.html'
......@@ -51,6 +51,16 @@ class BrowserTest < MiniTest::Test
print "Testing Vidjil client at " + index_path + "\n"
$b.goto index_path
if local_storage != nil and $b.driver.respond_to? :local_storage
$b.driver.execute_script("localStorage.clear();")
print "Set localStorage :\n"
local_storage.each do |key, value|
$b.driver.local_storage[key] = value
print " [" + key + "] => " + value+ "\n"
end
$b.refresh
end
# check that the browser loaded correctly
if not $b.div(:id => 'logo').present?
print "Loading of Vidjil client failed. Do not execute remaining tests."
......
......@@ -9,7 +9,7 @@ class TestGraph < BrowserTest
def setup
super
if not defined? $b
set_browser("/doc/analysis-example2.vidjil")
set_browser("/doc/analysis-example2.vidjil", nil, {"timeFormat" => "short_name" })
if $b.div(id: 'tip-container').present?
$b.div(:id => 'tip-container').div(:class => 'tip_1').element(:class => 'icon-cancel').click
end
......@@ -22,33 +22,42 @@ class TestGraph < BrowserTest
def test_00_starting_names
type_name = "SamplePlus" # by default if date given in vidjil file
type_name = "local_storage"
$b.div(:id => 'visu2_menu').hover
test_name_values(type_name)
sleep 0.5 #wait for menu to finish transition before starting
if $b.driver.respond_to? :local_storage
test_name_values(type_name)
end
end
def test_01_names_sample
type_name = "Sample"
type_name = "sampling_date"
change_name_key(type_name)
test_name_values(type_name)
end
def test_02_names_short
type_name = "Short"
type_name = "short_name"
change_name_key(type_name)
test_name_values(type_name)
end
def test_03_names_short
type_name = "Name"
def test_03_names
type_name = "name"
change_name_key(type_name)
test_name_values(type_name)
end
def test_04_delta_date
type_name = "delta_date"
change_name_key(type_name)
test_name_values(type_name)
end
# Not really a test
def test_zz_close
......@@ -61,40 +70,40 @@ class TestGraph < BrowserTest
def get_names(pos, type)
### names in various format
if pos == "0"
return 'T8045-BC081-Diag' if type == "Name"
return 'T8045-BC' if type == "Short" # short
return '2019-12-17' if type == "Sample" # date
return '2019-12-17' if type == "SamplePlus" # days
return 'T8045-BC081-Diag' if type == "name"
return 'T8045-BC' if type == "short_name" || type == "local_storage"
return '2019-12-17' if type == "sampling_date" # date
return '2019-12-17' if type == "delta_date" # days
elsif pos == "1"
return 'T8045-BC082-fu1' if type == "Name"
return 'T8045-BC' if type == "Short"
return '2019-12-27' if type == "Sample"
return '+10' if type == "SamplePlus"
return 'T8045-BC082-fu1' if type == "name"
return 'T8045-BC' if type == "short_name" || type == "local_storage"
return '2019-12-27' if type == "sampling_date"
return '+10' if type == "delta_date"
end
end
def change_name_key(type_name)
$b.menu_settings.click
$b.input(:id => "menuTimeForm"+type_name).click
$b.input(:id => "menuTimeForm_"+type_name).click
$b.div(:id => 'visu2_menu').click
$b.update_icon.wait_while(&:present?) # wait update
end
def test_name_values(type_name)
$b.div(:id => 'visu2_menu').hover
# By default, 2 samples are present in timeline graph
print "\nTest for: " + type_name
time0 = $b.graph_x_legend("0")
time1 = $b.graph_x_legend("1")
list0 = $b.td(:id => 'visu2_listElem_text_0' )
list1 = $b.td(:id => 'visu2_listElem_text_1' )
time0 = $b.graph_x_legend("0").text
time1 = $b.graph_x_legend("1").text
list0 = $b.td(:id => 'visu2_listElem_text_0' ).text
list1 = $b.td(:id => 'visu2_listElem_text_1' ).text
## In graph label
assert ( time0.text == get_names("0", type_name) ), "incorrect name show for first sample (graph label), expected " + get_names("0", type_name) + " got " + time0.text
assert ( time1.text == get_names("1", type_name) ), "incorrect name show for second sample (graph label), expected " + get_names("1", type_name) + " got " + time1.text
assert ( time0 == get_names("0", type_name) ), "incorrect name show for first sample (graph label), expected " + get_names("0", type_name) + " got " + time0
assert ( time1 == get_names("1", type_name) ), "incorrect name show for second sample (graph label), expected " + get_names("1", type_name) + " got " + time1
## In graph list
assert ( list0.text == get_names("0", type_name) ), "incorrect name show for first sample (graphList text), expected " + get_names("0", type_name) + " got " + time0.text
assert ( list1.text == get_names("1", type_name) ), "incorrect name show for second sample (graphList text), expected " + get_names("1", type_name) + " got " + time1.text
assert ( list0 == get_names("0", type_name) ), "incorrect name show for first sample (graphList text), expected " + get_names("0", type_name) + " got " + list0
assert ( list1 == get_names("1", type_name) ), "incorrect name show for second sample (graphList text), expected " + get_names("1", type_name) + " got " + list1
end
end
......@@ -656,6 +656,15 @@ only the logged-in users with proper authorization can access to these data.
This includes the uploader of the data,
possibly users of the same groups if such groups were defined, and the server maintainers.
# Settings
The settings menu allows to set:
-clone size format [scientific notation / percentage]
-sample key [sample name / shortened name / sampling date / day since first sampling]
-clone junction format [junction length / AA sequence / mixed (display AA sequence only for short junction)]
-clone alleles format [hide alleles / display alleles / mixed (display only for marginal alleles)]
These settings are kept in your web browser ``localStorage'' between several sessions.
# Keyboard shortcuts
......
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