Commit 509649f8 authored by Ryan Herbert's avatar Ryan Herbert

new stats view and results_file export

adds a simple sample_set_list that the user can select in order to
export all results_files for a given config that are associated to the
selected sample_sets.
The files are stored in a zip file along with a json file containing
some metadata.
parent 04a8695e
Pipeline #18110 passed with stage
in 2 seconds
......@@ -1099,6 +1099,16 @@ Database.prototype = {
return true;
},
updateStatsButton: function() {
var sample_set_ids = [];
$('[name^="sample_set_ids"]:checked').each(function() {
sample_set_ids.push("sample_set_ids=" + $(this).val());
});
var config_id = $('#choose_config').find(':selected').val();
var addr = DB_ADDRESS + '/sample_set/result_files?config_id=' + config_id + '&' + sample_set_ids.join('&');
$('#stats_button').attr('href', addr);
},
// Log functions, to server
// 'quiet' is set to true to avoid infinite loops with timeouts
log : function (lvl, msg) {
......
......@@ -212,10 +212,115 @@ def all():
step = step,
page = page)
def stats():
start = time.time()
if not auth.user :
res = {"redirect" : URL('default', 'user', args='login', scheme=True, host=True,
vars=dict(_next=URL('sample_set', 'all', vars={'type': defs.SET_TYPE_PATIENT}, scheme=True, host=True)))
}
return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
isAdmin = auth.is_admin()
if request.vars['type']:
type = request.vars['type']
else :
type = defs.SET_TYPE_GENERIC
##filter
if "filter" not in request.vars :
request.vars["filter"] = ""
search, tags = parse_search(request.vars["filter"])
group_ids = get_involved_groups()
list = SampleSetList(type, tags=tags)
list.load_sample_information()
list.load_anon_permissions()
result = list.get_values()
factory = ModelFactory()
helper = factory.get_instance(type=type)
fields = helper.get_reduced_fields()
##sort result
reverse = False
if request.vars["reverse"] == "true" :
reverse = True
if "sort" in request.vars:
result = sorted(result, key = lambda row : row[request.vars["sort"]], reverse=reverse)
else:
result = sorted(result, key = lambda row : row.id, reverse=not reverse)
result = helper.filter(search, result)
log.debug("%s stat list (%.3fs) %s" % (request.vars["type"], time.time()-start, search))
return dict(query = result,
fields = fields,
helper = helper,
group_ids = group_ids,
isAdmin = isAdmin,
reverse = False)
def result_files():
from zipfile import ZipFile
from cStringIO import StringIO
import types
errors = []
config_id = request.vars['config_id']
sample_set_ids = []
if 'sample_set_ids' in request.vars:
sample_set_ids = request.vars['sample_set_ids']
#little hack since we can't pass array parameters with only one value
if isinstance(sample_set_ids, types.StringTypes):
sample_set_ids = [sample_set_ids]
left_join = [
db.patient.on(db.patient.sample_set_id == db.sample_set.id),
db.run.on(db.run.sample_set_id == db.sample_set.id),
db.generic.on(db.generic.sample_set_id == db.sample_set.id)
]
q = db(
(db.sample_set.id.belongs(sample_set_ids)) &
(db.sample_set_membership.sample_set_id == db.sample_set.id) &
(db.results_file.sequence_file_id == db.sample_set_membership.sequence_file_id) &
(db.results_file.config_id == config_id)
)
results = q.select(db.results_file.ALL, db.sample_set.ALL, db.patient.ALL, db.run.ALL, db.generic.ALL, left=left_join)
sample_types = ['patient', 'run', 'generic']
mf = ModelFactory()
helpers = {}
for t in sample_types:
helpers[t] = mf.get_instance(type=t)
tempfile = StringIO()
zipfile = ZipFile(tempfile, 'w')
metadata = []
for res in results:
log.debug("res: " + str(res))
metadata.append({'id': res.sample_set.id,
'name': helpers[res.sample_set.sample_type].get_name(res[res.sample_set.sample_type]),
'file': res.results_file.data_file})
path = defs.DIR_RESULTS + res.results_file.data_file
zipfile.writestr(res.results_file.data_file, open(path, 'rb').read())
zipfile.writestr('metadata.json', json.dumps(metadata))
zipfile.close()
filename = "export_%s_%s.zip" % ('-'.join(sample_set_ids), str(datetime.date.today()))
response.headers['Content-Type'] = "application/zip"
response.headers['Content-Disposition'] = 'attachment; filename=%s' % filename# to force download as attachment
rtn = tempfile.getvalue()
return rtn
## Stats
def stats():
def mystats():
start = time.time()
# d = all()
......
......@@ -69,6 +69,13 @@ class SampleSet(object):
fields.append({'name': 'files', 'sort': 'file_count', 'call': self.get_files, 'width': 100, 'public': True})
return fields
def get_reduced_fields(self):
fields = []
fields.append({'name': 'name', 'sort': 'name', 'call': self.get_name, 'width': 200, 'public': True})
fields.append({'name': 'info', 'sort': 'info', 'call': self.get_tagged_info, 'width': None, 'public': True})
fields.append({'name': 'files', 'sort': 'file_count', 'call': self.get_files, 'width': 100, 'public': True})
return fields
def get_sequence_count(self, data):
if not hasattr(data, 'sequence_count'):
data.sequence_count = db( (db.sequence_file.id == db.sample_set_membership.sequence_file_id)
......
......@@ -10,6 +10,11 @@ class Patient(SampleSet):
fields.insert(1, {'name': 'birth', 'sort': 'birth', 'call': self.get_birth, 'width': 100, 'public': True})
return fields
def get_reduced_fields(self):
fields = super(Patient, self).get_reduced_fields()
fields.insert(1, {'name': 'birth', 'sort': 'birth', 'call': self.get_birth, 'width': 100, 'public': True})
return fields
def get_name(self, data, anon=None):
return vidjil_utils.anon_names(data.id, data.first_name, data.last_name, anon)
......
......@@ -7,6 +7,11 @@ class Run(SampleSet):
fields.insert(1, {'name': 'run_date', 'sort': 'run_date', 'call': self.get_run_date, 'width': 100, 'public': True})
return fields
def get_reduced_fields(self):
fields = super(Run, self).get_reduced_fields()
fields.insert(1, {'name': 'run_date', 'sort': 'run_date', 'call': self.get_run_date, 'width': 100, 'public': True})
return fields
def get_name(self, data):
return data.name
......
{{import vidjil_utils}}
<div>
<h3>Stats</h3>
<!--
<div class="db_block">
<div class="db_block_left">
search
<input id="db_filter_input" type="text" value="{{=request.vars["filter"]}}"
onchange="db.call('patient/stats', {'config_id' : '{{=request.vars["config_id"]}}',
'filter' : this.value,
'custom_list' : db.getListInput('custom_result[]')} )" >
</div>
<div class="db_block_right">
{{if auth.can_process_file() :}}
config
<span>
<select id="choose_config" name="config" onchange="db.call('patient/stats', {'config_id' : this.value,
'filter' : '{{=request.vars["filter"]}}',
'custom_list' : db.getListInput('custom_result[]') })">
<option value="-1" {{if not config :}}selected{{pass}}> --- </option>
{{for row in db((auth.vidjil_accessible_query(PermissionEnum.read_config.value, db.config) | auth.vidjil_accessible_query(PermissionEnum.admin_config.value, db.config) ) ).select(orderby=~db.config.name) :}}
<option value="{{=row.id }}" {{if row.id==config_id:}}selected{{pass}} >
{{=row.name}}
</option>
{{pass}}
</select>
</span>
{{pass}}
</div>
</div>
</div>
-->
<div id="db_table_container">
<table class="db_table" id="table" border="1">
<thead>
<tr> <!-- <td class="column1"> </td> -->
<td class="column_200"> patient </td>
<td class="column_200"> file name </td>
<!-- Stats -->
{{ for key in stats: }}
<td>{{ =key }}</td>
{{ pass }}
<!-- ----- -->
<td class="column1"> size </td>
<td class="column_200"> config </td>
<td class="column_sep"></td>
<td class="column2">last processing</td>
</tr>
</thead>
<tbody>
{{for row in query :}}
<tr>
<!-- <td> <input type="checkbox" name="custom_result[]" value="{{=row.results_file.id}}" {{if row.checked :}} checked {{pass}}> </td> -->
<td> {{=vidjil_utils.anon_names(row.sequence_file.patient_id, row.patient.first_name, row.patient.last_name)}}</td>
<td {{if row.sequence_file.data_file == None :}} {{=XML("class='inactive' title='file is missing' ")}} {{pass}} id="sequence_file_{{=row.sequence_file.id}}">
{{=row.sequence_file.filename}}
</td>
{{ for key in stats: }}
<td>
{{ if key in row: }}
{{ =row[key] }}
{{ pass }}
</td>
{{ pass }}
<td {{if row.sequence_file.data_file == None :}} {{=XML("class='inactive' title='file is missing' ")}} {{pass}} >
{{=vidjil_utils.format_size(row.sequence_file.size_file)}} </td>
<td> {{=row.config.name}}
<td class="column_sep"></td>
{{if row.results_file.run_date :}}
<td class="button" onclick="db.call('results_file/info', { 'results_file_id' : '{{=row.results_file.id}}' } )"> {{=row.results_file.run_date }}</td>
{{else:}}<td></td>{{pass}}
</tr>
{{pass}}
</tbody>
</table>
<table class="db_table" id="db_fixed_header"></table>
</div>
<!--
<div class="db_block">
<div class="db_block_left">
</div>
<div class="db_block_right">
<span class="button2" onclick="myUrl.loadCustomUrl(db)" > see results </span>
</div>
</div>
-->
</div>
......@@ -66,6 +66,7 @@
{{else:}}
<!-- <span class="button2 inactive" onclick="db.call('sample_set/add')" title="you don't have permission to create new {{=helper.get_type_display()}}s"> add sample_set </span> -->
{{pass}}
<span class="button2" onclick="db.call('sample_set/stats', {'type': '{{=helper.get_type()}}'})">stats</span>
</div>
<div class="db_block_right">
......
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