-
Notifications
You must be signed in to change notification settings - Fork 289
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #566 from camicroscope/export-page
Add an Export page
- Loading branch information
Showing
6 changed files
with
270 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<script src="../../core/Store.js"></script> | ||
<title>Export Results [caMicroscope]</title> | ||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous"> | ||
<style> | ||
body{ | ||
padding:20px; | ||
} | ||
#output{ | ||
padding: 20px; | ||
} | ||
span{ | ||
display:inline-block; | ||
} | ||
.form-check-input{ | ||
padding:5px !important; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
|
||
<h1>Export Results</h1> | ||
<div class="form"> | ||
<label for="slide_id">Slide Ids (exact matches, comma delimited):</label> | ||
<input type="text" id="slide_id" name="slide_id"><br> | ||
|
||
<button id="populate_btn" onclick="populateList()" type="button">Get List of Results</button> | ||
|
||
</div> | ||
<br><br> | ||
<h3>Select Results</h3> | ||
<div id="output"></div> | ||
<button id="convert_btn" onclick="downloadResults()" type="button">Download Selected Results</button> | ||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-u1OknCvxWvY5kfmNBILK2hRnQC3Pr17a+RTT6rIHI7NnikvbZlHgTPOOmMi466C8" crossorigin="anonymous"></script> | ||
<script src="//code.jquery.com/jquery.min.js"></script> | ||
<script src="./tree_table.js"></script> | ||
<script src="./export.js"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
const store = new Store('../../data/'); | ||
|
||
async function populateList() { | ||
// clear any previous | ||
document.getElementById('output').innerHTML = ''; | ||
console.log('populating list...'); | ||
nameField = 'name'; | ||
// get slide and associated result information | ||
let slides = []; | ||
let results = {}; | ||
let slideList = document.getElementById('slide_id').value; | ||
slideList = slideList.replace(/\s+/g, ''); | ||
slideList = slideList.split(','); | ||
for (let id of slideList) { | ||
let slide = await store.getSlide(id); | ||
slide = slide[0]; | ||
if (slide && slide['_id']) { | ||
let s = {'id': slide['_id']['$oid'], 'name': slide['name'], 'type': 'slide', 'raw': slide}; | ||
console.log(s); | ||
slides.push(s); | ||
// get associated result types | ||
r = []; | ||
for (let a of await store.findMarkTypes(slide['_id']['$oid'], 'computer')) { | ||
r.push({'id': a['_id']['analysis']['execution_id'], 'name': a['_id']['analysis']['name'], 'type': 'computer mark'}); | ||
} | ||
for (let a of await store.findMarkTypes(slide['_id']['$oid'], 'human')) { | ||
r.push({'id': a['_id']['analysis']['execution_id'], 'name': a['_id']['analysis']['name'], 'type': 'human mark'}); | ||
} | ||
// todo -- is this right for heatmapType results? | ||
for (let a of await store.findHeatmapType(slide['_id']['$oid'])) { | ||
r.push({'id': a['provenance']['analysis']['execution_id'], | ||
'name': a['provenance']['analysis']['execution_id'], 'type': 'heatmap'}); | ||
} | ||
results[slide['_id']['$oid']] = r; | ||
} | ||
} | ||
|
||
let headers = ['name', 'id', 'type']; | ||
let t = document.createElement('table'); | ||
t.id = 'tree-table'; | ||
t.classList.add('table', 'table-hover', 'table-bordered'); | ||
// add headers | ||
let table = document.createElement('tbody'); | ||
let headerTr = document.createElement('tr'); | ||
for (let z of headers) { | ||
let th = document.createElement('th'); | ||
th.innerText = z || '?'; | ||
headerTr.appendChild(th); | ||
} | ||
// add select header special | ||
let selectTh = document.createElement('th'); | ||
selectTh.innerText = 'Select'; | ||
headerTr.appendChild(selectTh); | ||
table.append(headerTr); | ||
// populate results | ||
for (let x of slides) { | ||
let parent = document.createElement('tr'); | ||
parent.setAttribute('data-id', x.id); | ||
parent.setAttribute('data-parent', 0); | ||
parent.setAttribute('data-level', 1); | ||
for (let z of headers) { | ||
let d = document.createElement('td'); | ||
d.innerText = x[z] || '?'; | ||
if (z==nameField) { | ||
d.setAttribute('data-column', 'name'); | ||
} | ||
parent.appendChild(d); | ||
} | ||
// add special checkbox | ||
parentCheck = document.createElement('input'); | ||
parentCheck.classList.add('form-check-input'); | ||
parentCheck.type = 'checkbox'; | ||
parentCheck.indeterminate = true; // cool! | ||
// TODO -- finish this. you'd want to add logic that sets this checkbox to true, false or indeterminate | ||
// depending on children selection. also select/deselect all children on change of this. | ||
// parent.appendChild(parentCheck); | ||
table.appendChild(parent); | ||
for (let y of results[x.id]) { | ||
console.log(x.raw); | ||
let child = document.createElement('tr'); | ||
child.setAttribute('data-id', x.id+'-'+y.id); | ||
child.setAttribute('data-parent', x.id); | ||
child.setAttribute('data-level', 2); | ||
for (let z of headers) { | ||
let d = document.createElement('td'); | ||
d.innerText = y[z] || '?'; | ||
if (z==nameField) { | ||
d.setAttribute('data-column', 'name'); | ||
} | ||
child.appendChild(d); | ||
} | ||
// special checkbox | ||
childCheck = document.createElement('input'); | ||
childCheck.type = 'checkbox'; | ||
childCheck.classList.add('form-check-input'); | ||
childCheck.classList.add('result'); | ||
childCheck.setAttribute('data-target', x.id); | ||
childCheck.setAttribute('data-self', y.id); | ||
childCheck.setAttribute('data-slideInfo', JSON.stringify(x.raw)); | ||
childCheck.setAttribute('data-type', y.type); | ||
childCheck.checked = true; | ||
child.appendChild(childCheck); | ||
table.appendChild(child); | ||
} | ||
} | ||
t.appendChild(table); | ||
document.getElementById('output').appendChild(t); | ||
makeTreeTable('tree-table'); | ||
} | ||
|
||
async function downloadResults() { | ||
let checks = document.querySelectorAll('.result:checked'); | ||
let marks = []; | ||
let heatmaps = []; | ||
for (let c of checks) { | ||
console.log(c.dataset); | ||
let parentSlide = JSON.parse(checks[0].dataset.slideinfo); | ||
if (c.dataset.type == 'human mark' || c.dataset.type == 'human mark') { | ||
let mark = await store.getMarkByIds([c.dataset.self], c.dataset.target); | ||
for (m of mark) { | ||
m.provenance.image = parentSlide; | ||
marks.push(m); | ||
} | ||
console.log(mark); | ||
} else if (c.dataset.type == 'heatmap' ) { | ||
let hm = await store.getHeatmap(c.dataset.parent, c.dataset.target); | ||
for (h of hm) { | ||
h.provenance.image = parentSlide; | ||
heatmaps.push(h); | ||
} | ||
} | ||
} | ||
console.log(marks, heatmaps); | ||
if (marks.length) { | ||
var element = document.createElement('a'); | ||
element.setAttribute('href', 'data:application/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(marks))); | ||
element.setAttribute('download', 'camic_export_marks.json'); | ||
element.style.display = 'none'; | ||
document.body.appendChild(element); | ||
element.click(); | ||
document.body.removeChild(element); | ||
} | ||
if (heatmaps.length) { | ||
var element = document.createElement('a'); | ||
element.setAttribute('href', 'data:application/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(heatmaps))); | ||
element.setAttribute('download', 'camic_export_heatmaps.json'); | ||
element.style.display = 'none'; | ||
document.body.appendChild(element); | ||
element.click(); | ||
document.body.removeChild(element); | ||
} | ||
// tell the user that data is missing | ||
if (!heatmaps.length && !marks.length) { | ||
alert('No data selected for download.'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
function makeTreeTable(id) { | ||
var | ||
$table = $('#' + id); | ||
var rows = $table.find('tr'); | ||
|
||
rows.each(function(index, row) { | ||
var | ||
$row = $(row); | ||
var level = $row.data('level'); | ||
var id = $row.data('id'); | ||
var $columnName = $row.find('td[data-column="name"]'); | ||
var children = $table.find('tr[data-parent="' + id + '"]'); | ||
|
||
if (children.length) { | ||
var expander = $columnName.prepend('' + | ||
'<span class="treegrid-expander glyphicon glyphicon-chevron-right">▼</span>' + | ||
''); | ||
|
||
children.hide(); | ||
|
||
expander.on('click', function(e) { | ||
var $target = $(e.target); | ||
if ($target.hasClass('glyphicon-chevron-right')) { | ||
$target | ||
.removeClass('glyphicon-chevron-right') | ||
.addClass('glyphicon-chevron-down'); | ||
|
||
children.show(); | ||
} else { | ||
$target | ||
.removeClass('glyphicon-chevron-down') | ||
.addClass('glyphicon-chevron-right'); | ||
|
||
reverseHide($table, $row); | ||
} | ||
}); | ||
} | ||
|
||
$columnName.prepend('' + | ||
'<span class="treegrid-indent" style="width:' + 25 * level + 'px"></span>' + | ||
''); | ||
}); | ||
|
||
// Reverse hide all elements | ||
reverseHide = function(table, element) { | ||
var | ||
$element = $(element); | ||
var id = $element.data('id'); | ||
var children = table.find('tr[data-parent="' + id + '"]'); | ||
|
||
if (children.length) { | ||
children.each(function(i, e) { | ||
reverseHide(table, e); | ||
}); | ||
|
||
$element | ||
.find('.glyphicon-chevron-down') | ||
.removeClass('glyphicon-chevron-down') | ||
.addClass('glyphicon-chevron-right'); | ||
|
||
children.hide(); | ||
} | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters