Commit dac836cc authored by Mathieu Giraud's avatar Mathieu Giraud

Merge remote-tracking branch 'origin/dev' into feature-s/more_stats_decorators

parents 0c12fdc7 92504489
......@@ -292,6 +292,8 @@ test_server_functional:
script:
- docker build --no-cache --build-arg git_branch=$CI_COMMIT_REF_NAME --build-arg build_env=TEST -t "vidjil/server:test" docker/vidjil-server
- docker build --no-cache --build-arg git_branch=$CI_COMMIT_REF_NAME --build-arg build_env=TEST -t "vidjil/client:test" docker/vidjil-client
- sed -i '/\/etc\/nginx\/ssl\:\/etc\/nginx\/ssl/d' ./docker/docker-compose.yml
- sed -i 's/\:latest/\:test/g' ./docker/docker-compose.yml
- cd docker && docker-compose up -d && cd ..
- sed -i "s/^python\ \.\.\/\.\.\/\.\./docker\ exec\ docker_uwsgi_1\ python\ \/usr\/share\/vidjil\/server\/web2py/" server/web2py/applications/vidjil/tests/init_func_test_db.sh
- docker exec docker_uwsgi_1 sed -i "s/^\(FILE_SOURCE .*\)/FILE_SOURCE = '\/usr\/share\/vidjil\/demo'/" /usr/share/vidjil/server/web2py/applications/vidjil/modules/defs.py
......@@ -300,6 +302,7 @@ test_server_functional:
- rvm use 2.1.1
- HEADLESS=1 make functional_server || (cd docker && docker-compose stop; false)
- cd docker && docker-compose stop
- docker rmi "vidjil/server:test" "vidjil/client:test"
only:
- /^feature-.*s.*\/.*$/
- /^hotfix-.*s.*\/.*$/
......
......@@ -16,7 +16,7 @@ SHOULD_LOG=$(SHOULD:.should-get=.tap)
SHOULD_VDJ=$(wildcard should-vdj-tests/*.should-vdj.fa)
SHOULD_VDJ_VDJ=$(SHOULD_VDJ:.should-vdj.fa=.1.vdj)
SHOULD_LOCUS=$(wildcard should-vdj-tests/*.should-locus.fa)
SHOULD_VDJ_TO_TAP=python -u should-vdj-to-tap.py
SHOULD_VDJ_TO_TAP=time python -u should-vdj-to-tap.py
REPORTS_PATH := $(patsubst %/Makefile,%,$(abspath $(lastword $(MAKEFILE_LIST))))/../../reports
VALGRIND=valgrind
VALGRIND_TOOLS= --tool=memcheck --leak-check=full --show-reachable=yes --trace-children=yes
......@@ -71,7 +71,7 @@ snapshot_diff_current:
should: vidjil
python3 should.py --xml $(SHOULD)
time python3 should.py --xml $(SHOULD)
shouldvdj_if_python:
if python ../../tools/check_python_version.py ; \
......@@ -122,7 +122,7 @@ curated-vdj.zip: $(SHOULD_VDJ_ARCHIVE)
valgrind_should: vidjil
$(MAKE) -C "$(REPORTS_PATH)" clean_valgrind
python3 should.py --launcher "$(VALGRIND_CMD)" $(SHOULD)
time python3 should.py --launcher "$(VALGRIND_CMD)" $(SHOULD)
valgrind_%:
LAUNCHER="$(VALGRIND_CMD)" $(MAKE) $(patsubst valgrind_%,%,$@)
......
......@@ -278,6 +278,7 @@ Axes.prototype = {
fct: function(clone) {return clone.getDeletion('3', 'delLeft')}
},
"occCloneDB": {
hide : true,
doc: "number of occurrences in cloneDB",
label: "cloneDB occurrences",
axis: new NumericalAxis(this.m),
......
......@@ -43,7 +43,7 @@ FormBuilder.prototype.build_label = function(txt, object, field) {
var l = document.createElement('label');
l.htmlFor = field + "_" + this.index;
l.id = object + "_" + field + "__label_" + this.index;
l.innerText = txt + ":";
$(l).text(txt + ":"); // for compatibility with older browsers (FF32, IE7/8)
return l;
}
......@@ -96,14 +96,14 @@ FormBuilder.prototype.build_div = function(type) {
d.appendChild(c);
var s = document.createElement('span');
s.className = "left form_label"
s.innerText = capitalise(type == 'generic' ? 'set' : type) + " " + (this.index+1);
$(s).text(capitalise(type == 'generic' ? 'set' : type) + " " + (this.index+1)); // for compatibility with older browsers (FF32, IE7/8)
d.appendChild(s);
return d;
}
FormBuilder.prototype.build_legend = function(text) {
var l = document.createElement('legend');
l.innerText = text;
$(l).text(text); // for compatibility with older browsers (FF32, IE7/8)
return l;
}
......@@ -187,7 +187,6 @@ PatientFormBuilder.prototype.createCloseButton = function() {
var close = Object.getPrototypeOf(PatientFormBuilder.prototype).createCloseButton.call(this);
$(close).click(function() {
var button = document.getElementById('patient_button');
button.dataset.index --;
});
return close;
};
......@@ -219,7 +218,6 @@ RunFormBuilder.prototype.createCloseButton = function() {
var close = Object.getPrototypeOf(RunFormBuilder.prototype).createCloseButton.call(this);
$(close).click(function() {
var button = document.getElementById('run_button');
button.dataset.index--;
});
return close;
};
......@@ -247,7 +245,6 @@ GenericFormBuilder.prototype.createCloseButton = function() {
var close = Object.getPrototypeOf(GenericFormBuilder.prototype).createCloseButton.call(this);
$(close).click(function() {
var button = document.getElementById('generic_button');
button.dataset.index--;
});
return close;
};
......@@ -390,7 +387,6 @@ FileFormBuilder.prototype.createCloseButton = function() {
var close = Object.getPrototypeOf(FileFormBuilder.prototype).createCloseButton.call(this);
$(close).click(function() {
var button = document.getElementById('file_button');
button.dataset.index--;
});
return close;
}
......@@ -42,6 +42,7 @@ function GenericAxis (reverse, can_undefined) {
this.reverse = false;
if(typeof reverse !== "undefined")
this.reverse = reverse
this.adapt = false
}
GenericAxis.prototype = {
......@@ -62,9 +63,14 @@ GenericAxis.prototype = {
this.populateLabels(labels, sort);
this.populateValueMapping();
return this;
},
ignore: function(clone) {
return this.adapt && clone.isFiltered
},
compareLabels: function(a, b) {
if (typeof a === 'undefined') return (typeof b === 'undefined') ? 0 : -1;
if (typeof b === 'undefined') return (typeof a === 'undefined') ? 0 : 1;
......@@ -86,6 +92,8 @@ GenericAxis.prototype = {
else {
for (var i=0; i < values.length; i++) {
var value = values[i];
if (this.ignore(value))
continue;
var convert = this.applyConverter(value);
var pos;
if (labels.indexOf(convert) != -1) {
......@@ -122,6 +130,8 @@ GenericAxis.prototype = {
// if (value.isVirtual())
// continue ; // ? pas toujours ?
if (this.ignore(value))
continue;
var convert = this.applyConverter(value);
if (typeof round !== 'undefined')
......@@ -207,6 +217,8 @@ GenericAxis.prototype = {
var has_undefined;
for (var i = 0; i < values.length; i++) {
var value = values[i];
if (this.ignore(value))
continue;
var key = this.applyConverter(value);
if (typeof key == 'undefined') {
has_undefined = true;
......
......@@ -81,6 +81,9 @@ NumericalAxis.prototype = Object.create(GenericAxis.prototype);
if (typeof labels == "undefined") {
for (var i in this.values){
if (this.ignore(this.values[i]))
continue;
if (! this.values[i].isVirtual()) {
var tmp = this.applyConverter(this.values[i]);
......
......@@ -1690,8 +1690,8 @@ ScatterPlot.prototype = {
this.mode = mode;
this.compute_size();
this.axisX = this.updateAxis(this.splitX, false);
this.axisY = this.updateAxis(this.splitY, true);
this.axisX = this.updateAxis(this.splitX, false, (this.mode == this.MODE_BAR));
this.axisY = this.updateAxis(this.splitY, true, false);
if (this.mode == this.MODE_BAR){
this.updateBar();
......@@ -1719,7 +1719,7 @@ ScatterPlot.prototype = {
* @param {Axis} axis
* @param {string} splitMethod
* */
updateAxis: function(splitMethod, is_Y) {
updateAxis: function(splitMethod, is_Y, adaptAxis) {
if (typeof is_Y === "undefined") is_Y = false;
var axis;
var aa = this.available_axis[splitMethod]
......@@ -1730,6 +1730,8 @@ ScatterPlot.prototype = {
axis = aa.axis;
axis.reverse = is_Y;
axis.adapt = adaptAxis
if (is_Y)
axis.MAX_NB_STEPS_IN_AXIS = 6
axis.init(this.m.clones, aa.fct, aa.labels, aa.sort, aa.min, aa.max, aa.log, aa.display_label);
......
......@@ -233,7 +233,8 @@ Segment.prototype = {
span = document.createElement('span');
span.id = "toCloneDB";
span.setAttribute('title', 'Send sequences to EC-NGS/CloneDB in the background')
span.className = "button devel-mode";
span.className = "button ";
span.className += (typeof config !== 'undefined' && config.clonedb) ? "" : "devel-mode";
span.onclick = function () {
self.db.callCloneDB(self.m.getSelected());
};
......
......@@ -95,12 +95,7 @@ def validate_sets(set_ids):
return sets, id_dict, errors
def get_pre_process_list():
query_pre_process = db(
db.pre_process>0
).select(
db.pre_process.ALL,
orderby = ~db.pre_process.id
)
query_pre_process = db((auth.vidjil_accessible_query(PermissionEnum.read_pre_process.value, db.pre_process) | auth.vidjil_accessible_query(PermissionEnum.admin_pre_process.value, db.pre_process) ) ).select(orderby=~db.pre_process.id)
pre_process_list = []
for row in query_pre_process :
......
......@@ -10,24 +10,31 @@ ACCESS_DENIED = "access denied"
## return group list
def index():
count = db.auth_group.id.count()
user_count = db.auth_user.id.count()
query = db(
db.auth_group.id > 0
).select(db.auth_group.ALL, groupby = db.auth_group.id, orderby=db.auth_group.role)
(db.auth_group.id > 0) &
(db.auth_membership.group_id == db.auth_group.id) &
(db.auth_membership.user_id == db.auth_user.id)
).select(
db.auth_group.role.with_alias('role'),
db.auth_group.id.with_alias('id'),
db.auth_group.description.with_alias('description'),
user_count.with_alias('count'),
groupby = db.auth_group.id,
orderby=db.auth_group.role)
for row in query:
row.count = db(
(db.auth_group.id == db.auth_membership.group_id)
& (db.auth_group.id == row.id)
).count()
row.parents = ', '.join(str(value) for value in auth.get_group_parent(row.id))
row.access = ''
if auth.has_permission(PermissionEnum.create.value, 'sample_set', group_id=row.id): row.access += 'c'
if auth.has_permission(PermissionEnum.upload.value, 'sample_set', group_id=row.id): row.access += 'u'
if auth.has_permission(PermissionEnum.run.value, 'sample_set', group_id=row.id): row.access += 'r'
if auth.has_permission(PermissionEnum.anon.value, 'sample_set', group_id=row.id): row.access += 'a'
if auth.has_permission(PermissionEnum.admin.value, 'sample_set', group_id=row.id): row.access += 'e'
if auth.has_permission(PermissionEnum.save.value, 'sample_set', group_id=row.id): row.access += 's'
permissions_list = [PermissionEnum.create.value,
PermissionEnum.upload.value,
PermissionEnum.run.value,
PermissionEnum.anon.value,
PermissionEnum.admin.value,
PermissionEnum.save.value]
permissions = auth.get_group_permissions(table_name='sample_set', group_id=row.id, myfilter=permissions_list)
row.access = ''.join([PermissionLetterMapping[p].value for p in permissions])
return dict(message=T('Groups'), query=query, count=count)
......
......@@ -115,3 +115,68 @@ def info():
else :
res = {"message": "acces denied"}
return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
def permission():
if (not auth.can_modify_pre_process(request.vars["id"]) ):
res = {"message": ACCESS_DENIED}
log.error(res)
return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
query = db( (db.auth_group.role != 'admin') ).select()
query2 = db( (db.auth_group.role != 'admin') &
(db.auth_membership.group_id == db.auth_group.id) &
(db.auth_membership.user_id == db.auth_user.id)
).select()
usermap = {}
for row in query2 :
if row.auth_group.role[:5] == "user_" :
usermap[row.auth_group.role] = row.auth_user.id
for row in query :
row.owner = row.role
if row.owner[:5] == "user_" :
id = usermap[row.owner]
row.owner = db.auth_user[id].first_name + " " + db.auth_user[id].last_name
permissions = db(
(db.auth_permission.group_id == row.id) &
(db.auth_permission.record_id == 0) &
(db.auth_permission.table_name == 'sample_set')).select()
row.perms = ', '.join(map(lambda x: x.name, permissions))
row.parent_access = ', '.join(str(value) for value in auth.get_access_groups(db.pre_process, request.vars['id'], group=row.id))
row.read = auth.get_group_access('pre_process', request.vars['id'], row.id)
return dict(query = query)
#TODO refactor with patient/change_permission
def change_permission():
if (not auth.can_modify_pre_process(request.vars["pre_process_id"]) ):
res = {"message": ACCESS_DENIED}
log.error(res)
return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
error = ""
if request.vars["group_id"] == "" :
error += "missing group_id, "
if request.vars["pre_process_id"] == "" :
error += "missing pre_process_id, "
if error=="":
if auth.get_group_access(db.pre_process, int(request.vars["pre_process_id"]), int(request.vars["group_id"])):
auth.del_permission(request.vars["group_id"], PermissionEnum.access.value, db.pre_process, request.vars["pre_process_id"])
res = {"message" : "c%s: access '%s' deleted to '%s'" % (request.vars["pre_process_id"],
PermissionEnum.access.value, db.auth_group[request.vars["group_id"]].role)}
else :
auth.add_permission(request.vars["group_id"], PermissionEnum.access.value, db.pre_process, request.vars["pre_process_id"])
res = {"message" : "c%s: access '%s' granted to '%s'" % (request.vars["pre_process_id"],
PermissionEnum.access.value, db.auth_group[request.vars["group_id"]].role)}
log.admin(res)
return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
else :
res = {"message": "incomplete request : "+error }
log.error(res)
return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
......@@ -451,6 +451,8 @@ def submit():
if length not in length_mapping:
length_mapping[length] = set_type
for p in data[set_type]:
if p is None:
continue
errors = helper.validate(p)
action = "add"
if len(errors) > 0:
......
#coding: utf-8
from gluon.tools import Auth
from gluon.dal import Row, Set, Query
from enum import Enum
from permission_enum import PermissionEnum
class PermissionLetterMapping(Enum):
admin = 'e'
create = 'c'
upload = 'u'
run = 'r'
anon = 'a'
save = 's'
class VidjilAuth(Auth):
admin = None
groups = None
......@@ -124,6 +133,15 @@ class VidjilAuth(Auth):
perm = self.has_permission(PermissionEnum.access.value, object_of_action, id, group_id = group)
return perm
def get_group_permissions(self, table_name, group_id, record_id=0, myfilter=[]):
q = ((db.auth_permission.table_name == table_name)&
(db.auth_permission.group_id == group_id)&
(db.auth_permission.record_id == record_id))
if myfilter:
q &= (db.auth_permission.name.belongs(myfilter))
perms = db(q).select(db.auth_permission.name, orderby=db.auth_permission.name)
return [p.name for p in perms]
def get_group_permission(self, action, object_of_action, id = 0, group = None):
'''
Returns whether the group has the permission to
......@@ -603,6 +621,7 @@ class VidjilAuth(Auth):
return query
def log_event(self, description, vars=None, origin='auth'):
super(VidjilAuth, self).log_event(description, vars, origin)
if self.log and description:
message = description % vars + '.'
for (k,v) in vars.iteritems():
......
......@@ -688,3 +688,11 @@ def init_db_helper(db, auth, force=False, admin_email="plop@plop.com", admin_pas
auth.add_permission(id_admin_group, PermissionEnum.create_config.value, db.config, 0)
auth.add_permission(id_admin_group, PermissionEnum.create_pre_process.value, db.pre_process, 0)
auth.add_permission(id_admin_group, 'impersonate', db.auth_user, 0)
auth.add_permission(id_public_group, PermissionEnum.read_config.value, db.config, 0)
for config in db(db.config.id > 0).select():
auth.add_permission(id_public_group, PermissionEnum.access.value, db.config, config.id)
auth.add_permission(id_public_group, PermissionEnum.read_pre_process.value, db.pre_process, 0)
for pre_process in db(db.pre_process.id > 0).select():
auth.add_permission(id_public_group, PermissionEnum.access.value, db.pre_process, pre_process.id)
......@@ -73,6 +73,7 @@ class DBInitialiser(object):
auth.add_permission(public_group.id, PermissionEnum.access.value, db.pre_process, pid)
for i in range(3):
db.pre_process.insert(name="test pre-process %d" % i, command="dummy &file1& &file2& > &result&", info="test %d" % i)
db.pre_process.insert(name="pre-process perm", command="dummy &file1& &file2& > &result&", info="dummy pre_process for permissions")
@_needs_sets
def _init_sequence_files(self):
......
......@@ -90,4 +90,28 @@ class TestPreProcess < ServerTest
lines = table.tbody.rows
assert(lines.count == count-1)
end
def test_pre_process_permission
table = go_to_list
line = table.td(:text => "pre-process perm").parent
line.i(:class => 'icon-key').click
Watir::Wait.until(30) {$b.execute_script("return jQuery.active") == 0}
list = $b.table(:id => "table")
list.wait_until_present
line = table.td(:text => "public").parent
checkbox = line.cells.last.checkbox
assert(checkbox.set? == false)
checkbox.click
# reload page to check if permissions are persistant.
$b.span(:id => "db_reload").click
Watir::Wait.until(30) {$b.execute_script("return jQuery.active") == 0}
list = $b.table(:id => "table")
list.wait_until_present
line = table.td(:text => "public").parent
checkbox = line.cells.last.checkbox
assert(checkbox.set?)
end
end
......@@ -43,17 +43,28 @@ class TestSampleSet < ServerTest
assert($b.select(:id => "group_select").present?)
# add more elements to form
for i in 0..3 do
for i in 0..4 do
$b.span(:id => "patient_button").click
# Delete a line to ensure robustness
if i == 2
l = $b.span(:text => "Patient %d" % (i+2)).parent
l.i(:class => "icon-cancel").click
end
end
# fill in form
for i in 0..4 do
form.text_field(:id => "patient_id_label_%d" % i).set("test_label %d" % i)
form.text_field(:id => "patient_first_name_%d"% i).set("first %d" % i)
form.text_field(:id => "patient_last_name_%d" % i).set("last %d" % i)
form.text_field(:id => "patient_birth_%d" % i).set("2010-10-10")
form.text_field(:id => "patient_info_%d" % i).set("patient %d #test #mytag%d" % [i, i])
if i > 2
k = i+1
else
k = i
end
form.text_field(:id => "patient_id_label_%d" % k).set("test_label %d" % k)
form.text_field(:id => "patient_first_name_%d"% k).set("first %d" % k)
form.text_field(:id => "patient_last_name_%d" % k).set("last %d" % k)
form.text_field(:id => "patient_birth_%d" % k).set("2010-10-10")
form.text_field(:id => "patient_info_%d" % k).set("patient %d #test #mytag%d" % [k, k])
end
form.input(:type => "submit").click
......
......@@ -683,3 +683,16 @@ class VidjilauthModel(unittest.TestCase):
res = auth.get_group_access('patient', patient_id, group_sec)
self.assertFalse(res, "Group %d should not have direct access to patient %d" % (group_sec, patient_id))
def testGetGroupPermissions(self):
permissions = auth.get_group_permissions(table_name='sample_set', group_id=parent_group)
expected = [PermissionEnum.read.value]
self.assertEqual(Counter(expected), Counter(permissions), "Expected %s, but got %s for group %d" % (str(expected), str(permissions), parent_group))
permissions = auth.get_group_permissions(table_name='sample_set', group_id=1)
expected = [PermissionEnum.access.value, PermissionEnum.read.value, PermissionEnum.admin.value, PermissionEnum.create.value]
self.assertEqual(Counter(expected), Counter(permissions), "Expected %s, but got %s for group %d" % (str(expected), str(permissions), 1))
expected = [PermissionEnum.read.value, PermissionEnum.create.value]
permissions = auth.get_group_permissions(table_name='sample_set', group_id=1, myfilter=expected)
self.assertEqual(Counter(expected), Counter(permissions), "Expected %s, but got %s for group %d" % (str(expected), str(permissions), 1))
{{extend 'db_layout.html'}}
{{import vidjil_utils}}
<h3>
Pre Process {{=db.pre_process[request.vars["id"]].name}}
</h3>
<div id="db_table_container">
<table class="db_table" id="table">
<thead>
<tr><td class="column1">id</td>
<td class="column_200"> group / user</td>
<td class="column_200">permissions</td>
<td class="column1">access from</td>
<td class="column1"> access</td>
</tr>
</thead>
{{admin = auth.can_modify_pre_process(request.vars["id"])}}
{{ for row in query :}}
<tr>
<td> {{=row.id}}</td>
<td> {{=row.owner}} </td>
<td> {{=row.perms}}</td>
<td> {{=row.parent_access}}
{{if admin :}}
<td> <input type="checkbox" onclick="db.call('pre_process/change_permission', {
'pre_process_id' : {{=request.vars['id']}} ,
'group_id' : {{=row.id}}})"
{{if row.read :}} checked {{pass}}></td>
{{else:}}
<td class="inactive"> <input type="checkbox" onclick="this.click()"
{{if row.read :}} checked {{pass}}></td>
{{pass}}
</tr>
{{pass}}
</table>
<table class="db_table" id="db_fixed_header"></table>
</div>
{{if not auth.can_modify_pre_process(request.vars["id"]) :}}
<div>you need admin access on this pre process if you want to change permission </div>
{{pass}}
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