Commit 32c97b7b authored by Mathieu Giraud's avatar Mathieu Giraud

Merge branch 'feature-sc/url_params' into 'dev'

Nicer URLs

Closes #2792

See merge request !720
parents 069f79a6 8f2c4cd6
Pipeline #152372 passed with stages
in 18 minutes and 25 seconds
......@@ -48,3 +48,5 @@ germline/germline-59.tar.gz
germline/IMGT_RELEASE
vidjil-algo
server/web2py/gluon/packages/dal/
......@@ -26,6 +26,7 @@
<link rel="shortcut icon" href="./images/favicon-v.ico" type="image/x-icon">
<link rel="icon" href="./images/favicon-v.ico" type="image/x-icon">
<link rel="stylesheet" type="text/css" href="css/fonts/ubuntu/stylesheet.css" />
<link rel="stylesheet" type="text/css" href="css/fonts/ubuntu-mono/stylesheet.css" />
<link rel="stylesheet" type="text/css" href="css/icons/fontello/css/fontello.css" />
......@@ -322,7 +323,6 @@
</div></div>
</div>
<div class="menu" id="alert"></div>
<div id="logo" onclick="console.log({'type': 'popup', 'default':'welcome'})">Vidjil <span class='logo'>(beta)</span></div>
<!--
......
var DEFAULT_DB_ADDRESS="https://db.vidjil.org/vidjil/";
requirejs.config({
baseUrl: 'js/lib',
baseUrl: './js/lib',
paths: {
app: '',
jquery: 'jquery-3.3.1.min',
......
......@@ -77,6 +77,7 @@ Model.prototype = {
build: function () {
var self =this;
this.waiting_screen_is_on = false;
this.waiting_screen = document.createElement("div");
this.waiting_screen.className = "waiting_screen";
......@@ -1304,10 +1305,15 @@ changeAlleleNotation: function(alleleNotation) {
* return true if a view has not finished an update
*/
updateIsPending:function(){
for (var i = 0; i < this.view.length; i++) {
//check if a view is waiting an update
for (var i = 0; i < this.view.length; i++)
if (this.view[i].updateIsPending())
return true;
}
//check waiting screen
if (this.waiting_screen_is_on)
return true;
return false;
},
......@@ -2629,12 +2635,16 @@ changeAlleleNotation: function(alleleNotation) {
this.waiting_screen.style.display = "block";
this.waiting_msg.innerHTML= text;
if (typeof shortcut != 'undefined') shortcut.on = false;
this.waiting_screen_is_on = true;
this.updateIcon();
},
resume: function(){
this.waiting_screen.style.display = "none";
this.waiting_msg.removeAllChildren();
if (typeof shortcut != 'undefined') shortcut.on = true;
this.waiting_screen_is_on = false;
this.updateIcon();
},
......
function Url(model, win) {
View.call(this, model);
this.m = model;
this.window = (typeof win != "undefined") ? win : window
......@@ -33,9 +32,9 @@ Url.prototype= {
}
// get sample_set/patient/run, config...
var straight_params = this.getStraightParams();
for (var i = 0; i < straight_params.length; i++) {
var p = straight_params[i];
var positionnal_params = this.getPositionnalParams();
for (var i = 0; i < positionnal_params.length; i++) {
var p = positionnal_params[i];
if (typeof this.m[p] !== "undefined") {
params_dict[p] = this.m[p];
}
......@@ -62,6 +61,7 @@ Url.prototype= {
/**
* update(size/style/position) a list of selected clones <br>
* a slight function for operation who impact only a bunch of clones (merge/split/...)
* @abstract
* @param {integer[]} list - array of clone index
* */
updateElem : function (list) {
......@@ -71,6 +71,7 @@ Url.prototype= {
/**
* update(style only) a list of selected clones <br>
* a slight function for operation who impact only styles of clones (select/focus)
* @abstract
* @param {integer[]} list - array of clone index
* */
updateElemStyle : function () {
......@@ -79,10 +80,10 @@ Url.prototype= {
applyURL : function() {
var straight_params = this.getStraightParams();
for (var i = 0; i < straight_params.length; i++) {
if (typeof this.url_dict[straight_params[i]] !== "undefined") {
this.m[straight_params[i]] = this.url_dict[straight_params[i]];
var positionnal_params = this.getPositionnalParams();
for (var i = 0; i < positionnal_params.length; i++) {
if (typeof this.url_dict[positionnal_params[i]] !== "undefined") {
this.m[positionnal_params[i]] = this.url_dict[positionnal_params[i]];
}
}
......@@ -90,7 +91,7 @@ Url.prototype= {
var clones = this.url_dict.clone.split(',');
for (var j = 0; j < clones.length; j++) {
var c = this.m.clone(clones[j]);
if (typeof c !== "undefined" && c.isInteractable()) {
if (typeof c !== "undefined" && !c.isVirtual()) {
c.select = true;
}
}
......@@ -117,7 +118,11 @@ Url.prototype= {
var p = params[tmparr[0]];
var key = this.encoder.decode(tmparr[0]);
var val = tmparr[1];
if (typeof p === "undefined") {
if ((key == "") /*empty keys are due to the use of "//" in url, ignore them*/ ||
(typeof val == "undefined")) {
//do nothing
}
else if (typeof p === "undefined") {
params[key] = val;
} else if (p.constructor === String){
params[key] = [];
......@@ -127,28 +132,42 @@ Url.prototype= {
params[key].push(val);
}
}
var url = this.window.location;
var positionnal_params = url.pathname.substr(1).split('-');
var pos_param_keys = this.getPositionnalParams();
if (positionnal_params.length > 1 && positionnal_params[0] != "index.html")
for (var j = 0; j < positionnal_params.length; j++)
params[pos_param_keys[j]] = positionnal_params[j];
return params
},
generateParamsString: function(params_dict) {
var params_list = [];
var positionnal_params = [];
for (var key in params_dict){
if ((typeof key != "undefined" && key !== "") && (typeof params_dict[key]!= "undefined")) {
var val = params_dict[key];
if ((typeof key != "undefined" && key !== "") && (typeof val != "undefined")) {
var pos = this.getPosition(key);
if (pos >= 0) {
positionnal_params[pos] = val;
} else {
var encoded = this.encoder.encode(key);
if (params_dict[key].constructor !== Array && params_dict[key] !== '') {
params_list.push(encoded+"="+params_dict[key])
} else if (params_dict[key].constructor === Array) {
for (var i = 0; i < params_dict[key].length; i++) {
params_list.push(encoded+"="+params_dict[key][i]);
if (val.constructor !== Array && val !== '') {
params_list.push(encoded+"="+val)
} else if (val.constructor === Array) {
for (var i = 0; i < val.length; i++) {
params_list.push(encoded+"="+val[i]);
}
}
}
}
}
return params_list.join("&");
return positionnal_params.join('-') + '?' + params_list.join("&");
},
pushUrl: function(params) {
var new_url = "?" + params;
var new_url = params;
try {
this.window.history.pushState('plop', 'plop', new_url);
} catch(error) {
......@@ -156,10 +175,14 @@ Url.prototype= {
}
},
getStraightParams: function() {
getPositionnalParams: function() {
return ["sample_set_id", "config"];
},
getPosition: function(param) {
return this.getPositionnalParams().indexOf(param);
},
loadUrl: function(db, args, filename) {
this.url_dict = args;
var newParams = this.generateParamsString(args);
......@@ -176,12 +199,13 @@ Url.prototype= {
};
Url.prototype = $.extend(Object.create(View.prototype), Url.prototype);
function UrlEncoder() {
this.encoding = {
'sample_set_id': 'set',
'patient_id': 'patient',
'run_id': 'run'
'run_id': 'run',
'config': 'conf'
};
this.decoding = {};
......
......@@ -3,16 +3,37 @@ QUnit.module("Url", {
});
/* ------------------------------------ */
var current_url = "mock://"
var initial_url = "mock://";
var current_url = initial_url;
function getSearch(url) {
var search = url.split('?')[1] ;
return (search == "" ? "" : '?' + search)
}
function getPathname(url) {
// remove protocol
var pathname = url.split('//');
pathname = pathname[pathname.length-1];
// remove search
pathname = pathname.split('?')[0];
// remove hostname
pathname = pathname.split('/');
return '/' + pathname.slice(1).join('/');
}
var windowMock = {
mocked: true,
location: {
search: {
toString: function() { var search = (current_url + '?').split('?')[1] ;
return (search == "" ? "" : '?' + search) }
}},
search: "",
pathname: ""
},
history: {
pushState: function(x, y, url) { current_url = url }
pushState: function(x, y, url) {
current_url = url;
windowMock.location.search = getSearch(url);
windowMock.location.pathname = getPathname(url);
}
}
};
windowMock.window = windowMock
......@@ -113,4 +134,61 @@ QUnit.test("plot : modifyURL",function (assert) { with (windowMock) {
}});
QUnit.test("url: parse", function(assert) { with (windowMock) {
windowMock.history.pushState('plop', 'plop', 'mock://foo.bar?param1=foo&param2=bar');
var m = new Model();
var url = new Url(m, windowMock);
var params = url.parseUrlParams('?param1=foo&param2=bar');
assert.deepEqual(params, {
"param1": "foo",
"param2": "bar"
}, "test url parse correct url");
windowMock.history.pushState('plop', 'plop', 'mock://foo.bar?fakeparam&realparam=real');
url = new Url(m, windowMock);
params = url.parseUrlParams('?fakeparam&realparam=real');
assert.deepEqual(params, {
'realparam': 'real'
});
}})
QUnit.test("url: generate", function(assert) { with (windowMock) {
var params = {
'param1': 'first',
'param2': 'second',
'param3': 'third'
};
var m = new Model();
var url = new Url(m, windowMock);
var param_string = url.generateParamsString(params);
assert.equal(param_string, "?param1=first&param2=second&param3=third");
}});
QUnit.test("url: positional parse", function(assert) { with (windowMock) {
var m = new Model();
windowMock.history.pushState('plop', 'plop', 'mock://foo.bar/1-3?param3=third');
var url = new Url(m, windowMock);
var params = url.parseUrlParams('?param3=third');
assert.deepEqual(params, {
'sample_set_id': '1',
'config': '3',
'param3': 'third'
});
}});
QUnit.test("url: positional generate", function(assert) { with (windowMock) {
var params = {
'sample_set_id': 1,
'config': 4,
'foobar': 'barfoo',
'param4': 'fourth'
};
var m = new Model();
var url = new Url(m, windowMock);
var param_string = url.generateParamsString(params);
assert.equal(param_string, '1-4?foobar=barfoo&param4=fourth');
}});
......@@ -32,6 +32,8 @@ class TestScatterplot < BrowserTest
# id 29 --> seg5; seg3 (IGHV1; IGHJ1)
def test_01_multiple_select_barmode
$b.menu_filter.click
$b.update_icon.wait_while(&:present?)
$b.send_keys 4
$b.update_icon.wait_while(&:present?)
# to verify correct selection, We will look in semgenter the presence if clone entrie
......@@ -57,6 +59,8 @@ class TestScatterplot < BrowserTest
end
def test_02_multiple_select_bubble
$b.menu_filter.click
$b.update_icon.wait_while(&:present?)
$b.send_keys 1
$b.update_icon.wait_while(&:present?)
# to verify correct selection, We will look in semgenter the presence if clone entrie
......
......@@ -31,6 +31,7 @@ run ln -s /etc/vidjil/nginx_gzip_static.conf /etc/nginx/conf.d/web2py/gzip_stati
run ln -s /etc/vidjil/nginx_gzip.conf /etc/nginx/conf.d/web2py/gzip.conf
run ln -s /etc/vidjil/uwsgi.conf /etc/nginx/conf.d/web2py/uwsgi.conf
run ln -s /etc/vidjil/germline.js /usr/share/vidjil/browser/js/germline.js
run ln -s /usr/share/vidjil/browser /usr/share/vidjil/b
copy ./scripts/nginx-entrypoint.sh /entrypoints/nginx-entrypoint.sh
run chown -R www-data:www-data /usr/share/vidjil
......
location / {
root /usr/share/vidjil/browser;
rewrite ^/([0-9]+)-([0-9]+)/(css|js|image|test)/(.*) /$3/$4 redirect;
rewrite ^/([0-9]+)-([0-9]+)/ /?set=$1&config=$2 redirect;
rewrite ^/([0-9]+)-([0-9]+) /?set=$1&config=$2 redirect;
expires 1h;
add_header Cache-Control must-revalidate;
error_page 405 = $uri;
}
location /browser {
root /usr/share/vidjil;
rewrite ^/browser/([0-9]+)-([0-9]+)/(css|js|image|test)/(.*) /browser/$3/$4 redirect;
rewrite ^/browser/([0-9]+)-([0-9]+)/ /browser/?set=$1&config=$2 redirect;
rewrite ^/browser/([0-9]+)-([0-9]+) /browser/?set=$1&config=$2 redirect;
expires 1h;
add_header Cache-Control must-revalidate;
error_page 405 = $uri;
}
location /germline {
root /usr/share/vidjil/;
expires 1h;
add_header Cache-Control must-revalidate;
error_page 405 = $uri;
}
......@@ -24,60 +24,8 @@ server {
keepalive_timeout 70;
uwsgi_max_temp_file_size 20480m;
location / {
include /etc/nginx/conf.d/web2py/uwsgi.conf;
proxy_read_timeout 600;
client_max_body_size 20G;
###
}
## if you serve static files through https, copy here the section
## from the previous server instance to manage static files
location /browser {
root /usr/share/vidjil;
expires 1h;
add_header Cache-Control must-revalidate;
error_page 405 = $uri;
}
location /germline {
root /usr/share/vidjil/;
expires 1h;
add_header Cache-Control must-revalidate;
error_page 405 = $uri;
}
###to enable correct use of response.static_version
#location ~* ^/(\w+)/static(?:/_[\d]+\.[\d]+\.[\d]+)?/(.*)$ {
# alias /usr/share/vidjil/server/web2py/applications/$1/static/$2;
# expires max;
#
#}
###
client_max_body_size 20G;
location /cgi/ {
gzip off;
root /usr/share/vidjil/browser/;
# Fastcgi socket
fastcgi_pass unix:/var/run/fcgiwrap.socket;
# Fastcgi parameters, include the standard ones
include /etc/nginx/fastcgi_params;
# Adjust non standard parameters (SCRIPT_FILENAME)
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location /vidjil/file/upload {
include /etc/nginx/conf.d/web2py/uwsgi.conf;
uwsgi_read_timeout 10m;
client_max_body_size 20G;
}
include /etc/vidjil/server_location.conf;
include /etc/vidjil/client_location.conf;
}
......@@ -7,60 +7,8 @@ server {
keepalive_timeout 70;
uwsgi_max_temp_file_size 20480m;
location / {
include /etc/nginx/conf.d/web2py/uwsgi.conf;
proxy_read_timeout 600;
client_max_body_size 20G;
###
}
## if you serve static files through https, copy here the section
## from the previous server instance to manage static files
location /browser {
root /usr/share/vidjil;
expires 1h;
add_header Cache-Control must-revalidate;
error_page 405 = $uri;
}
location /germline {
root /usr/share/vidjil/;
expires 1h;
add_header Cache-Control must-revalidate;
error_page 405 = $uri;
}
###to enable correct use of response.static_version
#location ~* ^/(\w+)/static(?:/_[\d]+\.[\d]+\.[\d]+)?/(.*)$ {
# alias /usr/share/vidjil/server/web2py/applications/$1/static/$2;
# expires max;
#
#}
###
client_max_body_size 20G;
location /cgi/ {
gzip off;
root /usr/share/vidjil/browser/;
# Fastcgi socket
fastcgi_pass unix:/var/run/fcgiwrap.socket;
# Fastcgi parameters, include the standard ones
include /etc/nginx/fastcgi_params;
# Adjust non standard parameters (SCRIPT_FILENAME)
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location /vidjil/file/upload {
include /etc/nginx/conf.d/web2py/uwsgi.conf;
uwsgi_read_timeout 10m;
client_max_body_size 20G;
}
include /etc/vidjil/server_location.conf;
include /etc/vidjil/client_location.conf;
}
location ~ ^/(vidjil|admin) {
include /etc/nginx/conf.d/web2py/uwsgi.conf;
proxy_read_timeout 600;
client_max_body_size 20G;
###
}
location /vidjil/file/upload {
include /etc/nginx/conf.d/web2py/uwsgi.conf;
uwsgi_read_timeout 10m;
client_max_body_size 20G;
}
\ No newline at end of file
......@@ -81,3 +81,4 @@ SET_TYPE_RUN= 'run'
SET_TYPE_GENERIC = 'generic'
REQUIRE_HTTPS = True
BROWSER_PATH = 'browser/index.html'
......@@ -78,3 +78,4 @@ SET_TYPE_RUN= 'run'
SET_TYPE_GENERIC = 'generic'
REQUIRE_HTTPS = False
BROWSER_PATH = 'browser/index.html'
......@@ -96,6 +96,14 @@ server {
uwsgi_temp_path /mnt/data/tmp;
location / {
rewrite /([0-9]+)/([0-9]+)(/+) /?set=$1&conf=$2 break;
root $CWD/../browser;
expires 1h;
error_page 405 = $uri;
}
location /vidjil {
include /etc/nginx/conf.d/web2py/uwsgi.conf
proxy_read_timeout 600;
client_max_body_size 20G;
......@@ -105,9 +113,10 @@ server {
## from the previous server instance to manage static files
location /browser {
rewrite /browser/([0-9]+)/([0-9]+)(\/?) /browser/?set=$1&conf=$2 break;
rewrite /browser/([0-9/]+)/(css|js|images|test)/(.*) /browser/$2/$3 redirect;
root $CWD/../;
expires 1h;
add_header Cache-Control must-revalidate;
error_page 405 = $uri;
}
......
......@@ -133,6 +133,10 @@ def index():
classification = getConfigsByClassification()
http_origin = ""
if request.env['HTTP_ORIGIN'] is not None:
http_origin = request.env['HTTP_ORIGIN'] + "/"
log.info('sample_set (%s)' % request.vars["id"], extra={'user_id': auth.user.id,
'record_id': request.vars["id"],
'table_name': "sample_set"})
......@@ -152,7 +156,8 @@ def index():
sample_type = db.sample_set[request.vars["id"]].sample_type,
config=config,
classification=classification,
tag_decorator=tag_decorator)
tag_decorator=tag_decorator,
http_origin=http_origin)
## return a list of generic sample_sets
def all():
......
......@@ -45,12 +45,15 @@ class SampleSet(object):
def get_config_urls(self, data):
configs = []
http_origin = ""
if request.env['HTTP_ORIGIN'] is not None:
http_origin = request.env['HTTP_ORIGIN'] + "/"
for conf in data['conf_list']:
filename = "(%s %s)" % (self.get_name(data), conf['name'])
if conf['fused_file'] is not None :
configs.append(
str(A(conf['name'],
_href="index.html?sample_set_id=%d&config=%d" % (data['sample_set_id'], conf['id']), _type="text/html",
_href=http_origin + "?sample_set_id=%d&config=%d" % (data['sample_set_id'], conf['id']), _type="text/html",
_onclick="event.preventDefault();event.stopPropagation();if( event.which == 2 ) { window.open(this.href); } else { myUrl.loadUrl(db, { 'sample_set_id' : '%d', 'config' : %d }, '%s' ); }" % (data['sample_set_id'], conf['id'], filename))))
else:
configs.append(conf['name'])
......
......@@ -96,3 +96,4 @@ FILE_SOURCE = '/mnt/data/src'
FILE_TYPES = ['fasta', 'fastq', 'fastq.gz', 'fa']
REQUIRE_HTTPS = True
BROWSER_PATH='browser/index.html'
......@@ -206,7 +206,7 @@
).select() :}}
{{if row.fused_file.fused_file is not None :}}
<a class="button2" href="index.html?sample_set_id={{=request.vars["id"]}}&config={{=row.config.id}}" type="text/html"
<a class="button2" href="{{=http_origin}}?sample_set_id={{=request.vars["id"]}}&config={{=row.config.id}}" type="text/html"
onclick="event.preventDefault()
if( event.which == 2 ) { window.open(this.href) }
else { myUrl.loadUrl(db, { 'sample_set_id' : '{{=request.vars["id"]}}' , 'config' : {{=row.config.id}} }, '{{=info["filename"]}}' ) }" >
......
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