From ca71248f530c72763b0af8aa8c42f26ed0a27f17 Mon Sep 17 00:00:00 2001 From: Ryan Birmingham Date: Wed, 7 Sep 2022 22:04:43 -0400 Subject: [PATCH 01/15] concept for result selection --- apps/port/export.html | 29 +++++++++++++++++++ apps/port/export.js | 47 ++++++++++++++++++++++++++++++ apps/port/import.html | 30 +++++++++++++++++++ apps/port/import.js | 22 ++++++++++++++ apps/port/tree_table.js | 64 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 192 insertions(+) create mode 100644 apps/port/export.html create mode 100644 apps/port/export.js create mode 100644 apps/port/import.html create mode 100644 apps/port/import.js create mode 100644 apps/port/tree_table.js diff --git a/apps/port/export.html b/apps/port/export.html new file mode 100644 index 000000000..fd631874d --- /dev/null +++ b/apps/port/export.html @@ -0,0 +1,29 @@ + + + + + + Export Results [caMicroscope] + + + + +

Export Results

+

Info

+
+ +
+ +
+ + + +
+

+

Output

+
+ + + + + diff --git a/apps/port/export.js b/apps/port/export.js new file mode 100644 index 000000000..38f5736a0 --- /dev/null +++ b/apps/port/export.js @@ -0,0 +1,47 @@ +const store = new Store('../../data'); + +function populateList() { + // testing the tree table + let res = [{"name":'a', "id":1}, {"name":'b', "id":2}, {"name":'c', "id":3}]; + let annots = [{"name":'circles', "type": "mark", "id":"x1"}, {"name":'boxes', "type": "heatmap", "id":"x2"}] + let headers = ["name", "id"] + let table = document.createElement("table"); + table.id = "result_table"; + table.classList.add("table", "table-hover", "table-bordered"); + // add headers + for (let z of headers){ + let th = document.createElement("th"); + th.innerText = z || "?" + table.appendChild(th); + } + for (let x of res){ + console.log(x) + 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] || "?"; + parent.appendChild(d); + } + table.appendChild(parent) + for (let y of annots){ + let child = document.createElement("tr"); + child.setAttribute("data-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] || "?"; + child.appendChild(d); + } + table.appendChild(child) + } + } + document.getElementById("output").appendChild(table); +} + +function downloadResults() { + console.log('hi again') +} diff --git a/apps/port/import.html b/apps/port/import.html new file mode 100644 index 000000000..920dc773c --- /dev/null +++ b/apps/port/import.html @@ -0,0 +1,30 @@ + + + + + + + + Import and Export [caMicroscope] + + +

Import Annotations

+

Info

+
+ +
+ +
+

File Contents

+ +
+ + +
+

+

Output

+ +
+ + + diff --git a/apps/port/import.js b/apps/port/import.js new file mode 100644 index 000000000..f487d604f --- /dev/null +++ b/apps/port/import.js @@ -0,0 +1,22 @@ +const store = new Store('../../data'); + +function prepareAnnot() { + try { + let output = JSON.parse(document.getElementById('input')); + output['provenance']['image']['slide'] = document.getElementById('slide_id').value; + output['provenance']['analysis']['execution'] = document.getElementById('annot_name').value; + output['properties']['annotations']['name'] = document.getElementById('annot_name').value; + } catch (e) { + alert(e); + } + document.getElementById('output').innerHTML = JSON.stringify(output); +} + +function saveAnnot() { + try { + let doc = JSON.parse(document.getElementById('output')); + } catch (e) { + alert(e); + } + store.addMark(doc).then((x)=>alert('done!')).catch((e)=>alert(e)); +} diff --git a/apps/port/tree_table.js b/apps/port/tree_table.js new file mode 100644 index 000000000..01930e021 --- /dev/null +++ b/apps/port/tree_table.js @@ -0,0 +1,64 @@ +$(function () { + var + $table = $('#tree-table'), + rows = $table.find('tr'); + + rows.each(function (index, row) { + var + $row = $(row), + level = $row.data('level'), + id = $row.data('id'), + $columnName = $row.find('td[data-column="name"]'), + children = $table.find('tr[data-parent="' + id + '"]'); + + if (children.length) { + var expander = $columnName.prepend('' + + '' + + ''); + + 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('' + + '' + + ''); + }); + + // Reverse hide all elements + reverseHide = function (table, element) { + var + $element = $(element), + id = $element.data('id'), + 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(); + } + }; +}); From 4a572ad58437a710c34749078b0df28ab5d8a091 Mon Sep 17 00:00:00 2001 From: Ryan Birmingham Date: Wed, 7 Sep 2022 22:23:36 -0400 Subject: [PATCH 02/15] modify treetable some --- apps/port/export.html | 2 +- apps/port/export.js | 18 ++++++++++++------ apps/port/tree_table.js | 7 ++++--- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/apps/port/export.html b/apps/port/export.html index fd631874d..025fc1c2a 100644 --- a/apps/port/export.html +++ b/apps/port/export.html @@ -2,7 +2,6 @@ - Export Results [caMicroscope] @@ -25,5 +24,6 @@

Output

+ diff --git a/apps/port/export.js b/apps/port/export.js index 38f5736a0..644af7ccc 100644 --- a/apps/port/export.js +++ b/apps/port/export.js @@ -1,19 +1,23 @@ const store = new Store('../../data'); function populateList() { + console.log("populating list...") // testing the tree table let res = [{"name":'a', "id":1}, {"name":'b', "id":2}, {"name":'c', "id":3}]; let annots = [{"name":'circles', "type": "mark", "id":"x1"}, {"name":'boxes', "type": "heatmap", "id":"x2"}] let headers = ["name", "id"] - let table = document.createElement("table"); - table.id = "result_table"; - table.classList.add("table", "table-hover", "table-bordered"); + 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 hdr_tr = document.createElement("tr"); for (let z of headers){ let th = document.createElement("th"); th.innerText = z || "?" - table.appendChild(th); + hdr_tr.appendChild(th); } + table.appendChild(hdr_tr); for (let x of res){ console.log(x) let parent = document.createElement("tr"); @@ -28,7 +32,7 @@ function populateList() { table.appendChild(parent) for (let y of annots){ let child = document.createElement("tr"); - child.setAttribute("data-id", y.id); + child.setAttribute("data-id", x.id+"-"+y.id); child.setAttribute("data-parent", x.id); child.setAttribute("data-level", 2); for (let z of headers){ @@ -39,7 +43,9 @@ function populateList() { table.appendChild(child) } } - document.getElementById("output").appendChild(table); + t.appendChild(table); + document.getElementById("output").appendChild(t); + make_tree_table("tree-table") } function downloadResults() { diff --git a/apps/port/tree_table.js b/apps/port/tree_table.js index 01930e021..d00422e5d 100644 --- a/apps/port/tree_table.js +++ b/apps/port/tree_table.js @@ -1,6 +1,7 @@ -$(function () { +function make_tree_table(id){ + console.log("tree table") var - $table = $('#tree-table'), + $table = $('#' + id), rows = $table.find('tr'); rows.each(function (index, row) { @@ -61,4 +62,4 @@ $(function () { children.hide(); } }; -}); +} From 15236bd156844b452ac9ddd36f36d99668740c04 Mon Sep 17 00:00:00 2001 From: Ryan Birmingham Date: Wed, 7 Sep 2022 22:40:20 -0400 Subject: [PATCH 03/15] working tree table, finally --- apps/port/export.html | 16 ++++++++++++++-- apps/port/export.js | 8 +++++++- apps/port/index.html | 30 ------------------------------ apps/port/port.js | 22 ---------------------- apps/port/tree_table.js | 3 +-- 5 files changed, 22 insertions(+), 57 deletions(-) delete mode 100644 apps/port/index.html delete mode 100644 apps/port/port.js diff --git a/apps/port/export.html b/apps/port/export.html index 025fc1c2a..be904a027 100644 --- a/apps/port/export.html +++ b/apps/port/export.html @@ -4,6 +4,17 @@ Export Results [caMicroscope] + @@ -16,11 +27,12 @@

Info


- +

-

Output

+

Select Results

+ diff --git a/apps/port/export.js b/apps/port/export.js index 644af7ccc..6c4ae7e92 100644 --- a/apps/port/export.js +++ b/apps/port/export.js @@ -2,6 +2,7 @@ const store = new Store('../../data'); function populateList() { console.log("populating list...") + name_field = "name" // testing the tree table let res = [{"name":'a', "id":1}, {"name":'b', "id":2}, {"name":'c', "id":3}]; let annots = [{"name":'circles', "type": "mark", "id":"x1"}, {"name":'boxes', "type": "heatmap", "id":"x2"}] @@ -19,7 +20,6 @@ function populateList() { } table.appendChild(hdr_tr); for (let x of res){ - console.log(x) let parent = document.createElement("tr"); parent.setAttribute("data-id", x.id); parent.setAttribute("data-parent", 0); @@ -27,6 +27,9 @@ function populateList() { for (let z of headers){ let d = document.createElement("td"); d.innerText = x[z] || "?"; + if (z==name_field){ + d.setAttribute("data-column","name"); + } parent.appendChild(d); } table.appendChild(parent) @@ -38,6 +41,9 @@ function populateList() { for (let z of headers){ let d = document.createElement("td"); d.innerText = y[z] || "?"; + if (z==name_field){ + d.setAttribute("data-column","name"); + } child.appendChild(d); } table.appendChild(child) diff --git a/apps/port/index.html b/apps/port/index.html deleted file mode 100644 index 785b08ae4..000000000 --- a/apps/port/index.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - Import and Export [caMicroscope] - - -

Import Annotations

-

Info

-
- -
- -
-

File Contents

- -
- - -
-

-

Output

- -
- - - diff --git a/apps/port/port.js b/apps/port/port.js deleted file mode 100644 index f487d604f..000000000 --- a/apps/port/port.js +++ /dev/null @@ -1,22 +0,0 @@ -const store = new Store('../../data'); - -function prepareAnnot() { - try { - let output = JSON.parse(document.getElementById('input')); - output['provenance']['image']['slide'] = document.getElementById('slide_id').value; - output['provenance']['analysis']['execution'] = document.getElementById('annot_name').value; - output['properties']['annotations']['name'] = document.getElementById('annot_name').value; - } catch (e) { - alert(e); - } - document.getElementById('output').innerHTML = JSON.stringify(output); -} - -function saveAnnot() { - try { - let doc = JSON.parse(document.getElementById('output')); - } catch (e) { - alert(e); - } - store.addMark(doc).then((x)=>alert('done!')).catch((e)=>alert(e)); -} diff --git a/apps/port/tree_table.js b/apps/port/tree_table.js index d00422e5d..8dfb8982b 100644 --- a/apps/port/tree_table.js +++ b/apps/port/tree_table.js @@ -1,5 +1,4 @@ function make_tree_table(id){ - console.log("tree table") var $table = $('#' + id), rows = $table.find('tr'); @@ -14,7 +13,7 @@ function make_tree_table(id){ if (children.length) { var expander = $columnName.prepend('' + - '' + + '' + ''); children.hide(); From d3533fb4818097b71fde93b06009daab54c96ad4 Mon Sep 17 00:00:00 2001 From: Ryan Birmingham Date: Wed, 7 Sep 2022 22:52:46 -0400 Subject: [PATCH 04/15] visual design --- apps/port/export.html | 3 +++ apps/port/export.js | 25 ++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/apps/port/export.html b/apps/port/export.html index be904a027..72b153193 100644 --- a/apps/port/export.html +++ b/apps/port/export.html @@ -14,6 +14,9 @@ span{ display:inline-block; } + .form-check-input{ + padding:5px !important; + } diff --git a/apps/port/export.js b/apps/port/export.js index 6c4ae7e92..43d851bdc 100644 --- a/apps/port/export.js +++ b/apps/port/export.js @@ -1,12 +1,14 @@ const store = new Store('../../data'); function populateList() { + // clear any previous + document.getElementById("output").innerHTML = ""; console.log("populating list...") name_field = "name" // testing the tree table - let res = [{"name":'a', "id":1}, {"name":'b', "id":2}, {"name":'c', "id":3}]; + let res = [{"name":'a', "type": "slide", "id":1}, {"name":'b', "type": "slide", "id":2}, {"name":'c', "type": "slide", "id":3}]; let annots = [{"name":'circles', "type": "mark", "id":"x1"}, {"name":'boxes', "type": "heatmap", "id":"x2"}] - let headers = ["name", "id"] + let headers = ["name", "id", "type"] let t = document.createElement("table"); t.id = "tree-table"; t.classList.add("table", "table-hover", "table-bordered"); @@ -18,7 +20,12 @@ function populateList() { th.innerText = z || "?" hdr_tr.appendChild(th); } - table.appendChild(hdr_tr); + // add select header special + let select_th = document.createElement("th"); + select_th.innerText = "Select"; + hdr_tr.appendChild(select_th); + table.append(hdr_tr); + // populate results for (let x of res){ let parent = document.createElement("tr"); parent.setAttribute("data-id", x.id); @@ -32,6 +39,12 @@ function populateList() { } parent.appendChild(d); } + // add special checkbox + parentCheck = document.createElement("input"); + parentCheck.classList.add("form-check-input") + parentCheck.type = "checkbox" + parentCheck.checked = true; + parent.appendChild(parentCheck); table.appendChild(parent) for (let y of annots){ let child = document.createElement("tr"); @@ -46,6 +59,12 @@ function populateList() { } child.appendChild(d); } + // special checkbox + childCheck = document.createElement("input"); + childCheck.type = "checkbox" + childCheck.classList.add("form-check-input") + childCheck.checked = true; + child.appendChild(childCheck); table.appendChild(child) } } From e906d292c5c13b0e227a2878c9844ff6733a7ca4 Mon Sep 17 00:00:00 2001 From: Ryan Birmingham Date: Fri, 9 Sep 2022 20:24:57 -0400 Subject: [PATCH 05/15] download selector sans checkbox heirarchy auto --- apps/port/export.html | 2 -- apps/port/export.js | 44 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/apps/port/export.html b/apps/port/export.html index 72b153193..69fa6a0ab 100644 --- a/apps/port/export.html +++ b/apps/port/export.html @@ -26,8 +26,6 @@

Info


- -
diff --git a/apps/port/export.js b/apps/port/export.js index 43d851bdc..087cb5386 100644 --- a/apps/port/export.js +++ b/apps/port/export.js @@ -1,11 +1,39 @@ -const store = new Store('../../data'); +const store = new Store('../../data/'); -function populateList() { +async function populateList() { // clear any previous document.getElementById("output").innerHTML = ""; console.log("populating list...") name_field = "name" - // testing the tree table + // 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 (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"} + 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 res = [{"name":'a', "type": "slide", "id":1}, {"name":'b', "type": "slide", "id":2}, {"name":'c', "type": "slide", "id":3}]; let annots = [{"name":'circles', "type": "mark", "id":"x1"}, {"name":'boxes', "type": "heatmap", "id":"x2"}] let headers = ["name", "id", "type"] @@ -26,7 +54,7 @@ function populateList() { hdr_tr.appendChild(select_th); table.append(hdr_tr); // populate results - for (let x of res){ + for (let x of slides){ let parent = document.createElement("tr"); parent.setAttribute("data-id", x.id); parent.setAttribute("data-parent", 0); @@ -43,10 +71,10 @@ function populateList() { parentCheck = document.createElement("input"); parentCheck.classList.add("form-check-input") parentCheck.type = "checkbox" - parentCheck.checked = true; + parentCheck.indeterminate = true; // cool! parent.appendChild(parentCheck); table.appendChild(parent) - for (let y of annots){ + for (let y of results[x.id]){ let child = document.createElement("tr"); child.setAttribute("data-id", x.id+"-"+y.id); child.setAttribute("data-parent", x.id); @@ -63,6 +91,10 @@ function populateList() { 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-type", y.type); childCheck.checked = true; child.appendChild(childCheck); table.appendChild(child) From 831f9c51334aaa66f3414c03a9e44b4c0b4c2ffb Mon Sep 17 00:00:00 2001 From: Ryan Birmingham Date: Fri, 9 Sep 2022 21:02:47 -0400 Subject: [PATCH 06/15] iniial working version of export tool --- apps/port/export.html | 1 - apps/port/export.js | 57 +++++++++++++++++++++++++++++++++++++---- apps/port/tree_table.js | 2 +- 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/apps/port/export.html b/apps/port/export.html index 69fa6a0ab..4d8e3d82e 100644 --- a/apps/port/export.html +++ b/apps/port/export.html @@ -22,7 +22,6 @@

Export Results

-

Info


diff --git a/apps/port/export.js b/apps/port/export.js index 087cb5386..37253b00a 100644 --- a/apps/port/export.js +++ b/apps/port/export.js @@ -11,11 +11,11 @@ async function populateList() { let slideList = document.getElementById("slide_id").value; slideList = slideList.replace(/\s+/g, ''); slideList = slideList.split(",") - for (id of slideList){ + 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"} + let s = {"id": slide["_id"]["$oid"], "name": slide['name'], "type": "slide", "raw":slide} console.log(s) slides.push(s) // get associated result types @@ -72,9 +72,12 @@ async function populateList() { parentCheck.classList.add("form-check-input") parentCheck.type = "checkbox" parentCheck.indeterminate = true; // cool! - parent.appendChild(parentCheck); + // 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); @@ -94,6 +97,7 @@ async function populateList() { 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); @@ -105,6 +109,49 @@ async function populateList() { make_tree_table("tree-table") } -function downloadResults() { - console.log('hi again') +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){ + 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){ + 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 && !marks){ + alert("No data selected for download.") + } } diff --git a/apps/port/tree_table.js b/apps/port/tree_table.js index 8dfb8982b..a832be046 100644 --- a/apps/port/tree_table.js +++ b/apps/port/tree_table.js @@ -37,7 +37,7 @@ function make_tree_table(id){ } $columnName.prepend('' + - '' + + '' + ''); }); From 3bba3f354bc5da005a1640949852fdf4a0b961de Mon Sep 17 00:00:00 2001 From: Ryan Birmingham Date: Fri, 9 Sep 2022 21:04:17 -0400 Subject: [PATCH 07/15] Add link to export --- apps/table.html | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/table.html b/apps/table.html index 10e2b825a..7590fff4f 100644 --- a/apps/table.html +++ b/apps/table.html @@ -64,6 +64,9 @@ + @@ -136,7 +139,7 @@

caMicroscope

onclick="(()=>{location.reload();return false;})()"> Reload
-
+
@@ -149,12 +152,12 @@

caMicroscope

- +
- + - + @@ -295,7 +298,7 @@