Skip to content

Commit 00e86f1

Browse files
authored
Merge pull request #568 from camicroscope/develop
For 3.10.0
2 parents 9df777b + 83a43e9 commit 00e86f1

File tree

11 files changed

+302
-31
lines changed

11 files changed

+302
-31
lines changed

.github/workflows/node.js.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212

1313
strategy:
1414
matrix:
15-
node-version: [10.x, 12.x, 14.x, 15.x]
15+
node-version: [14.x, 16.x, 18.x]
1616
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
1717

1818
steps:

apps/port/export.html

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<script src="../../core/Store.js"></script>
5+
<title>Export Results [caMicroscope]</title>
6+
<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">
7+
<style>
8+
body{
9+
padding:20px;
10+
}
11+
#output{
12+
padding: 20px;
13+
}
14+
span{
15+
display:inline-block;
16+
}
17+
.form-check-input{
18+
padding:5px !important;
19+
}
20+
</style>
21+
</head>
22+
<body>
23+
24+
<h1>Export Results</h1>
25+
<div class="form">
26+
<label for="slide_id">Slide Ids (exact matches, comma delimited):</label>
27+
<input type="text" id="slide_id" name="slide_id"><br>
28+
29+
<button id="populate_btn" onclick="populateList()" type="button">Get List of Results</button>
30+
31+
</div>
32+
<br><br>
33+
<h3>Select Results</h3>
34+
<div id="output"></div>
35+
<button id="convert_btn" onclick="downloadResults()" type="button">Download Selected Results</button>
36+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-u1OknCvxWvY5kfmNBILK2hRnQC3Pr17a+RTT6rIHI7NnikvbZlHgTPOOmMi466C8" crossorigin="anonymous"></script>
37+
<script src="//code.jquery.com/jquery.min.js"></script>
38+
<script src="./tree_table.js"></script>
39+
<script src="./export.js"></script>
40+
</body>
41+
</html>

apps/port/export.js

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
const store = new Store('../../data/');
2+
3+
async function populateList() {
4+
// clear any previous
5+
document.getElementById('output').innerHTML = '';
6+
console.log('populating list...');
7+
nameField = 'name';
8+
// get slide and associated result information
9+
let slides = [];
10+
let results = {};
11+
let slideList = document.getElementById('slide_id').value;
12+
slideList = slideList.replace(/\s+/g, '');
13+
slideList = slideList.split(',');
14+
for (let id of slideList) {
15+
let slide = await store.getSlide(id);
16+
slide = slide[0];
17+
if (slide && slide['_id']) {
18+
let s = {'id': slide['_id']['$oid'], 'name': slide['name'], 'type': 'slide', 'raw': slide};
19+
console.log(s);
20+
slides.push(s);
21+
// get associated result types
22+
r = [];
23+
for (let a of await store.findMarkTypes(slide['_id']['$oid'], 'computer')) {
24+
console.log(a);
25+
r.push({'id': a['execution_id'], 'name': a['name'], 'type': 'computer mark'});
26+
}
27+
for (let a of await store.findMarkTypes(slide['_id']['$oid'], 'human')) {
28+
console.log(a);
29+
r.push({'id': a['_id']['analysis']['execution_id'], 'name': a['_id']['analysis']['name'], 'type': 'human mark'});
30+
}
31+
// todo -- is this right for heatmapType results?
32+
for (let a of await store.findHeatmapType(slide['_id']['$oid'])) {
33+
r.push({'id': a['provenance']['analysis']['execution_id'],
34+
'name': a['provenance']['analysis']['execution_id'], 'type': 'heatmap'});
35+
}
36+
results[slide['_id']['$oid']] = r;
37+
}
38+
}
39+
40+
let headers = ['name', 'id', 'type'];
41+
let t = document.createElement('table');
42+
t.id = 'tree-table';
43+
t.classList.add('table', 'table-hover', 'table-bordered');
44+
// add headers
45+
let table = document.createElement('tbody');
46+
let headerTr = document.createElement('tr');
47+
for (let z of headers) {
48+
let th = document.createElement('th');
49+
th.innerText = z || '?';
50+
headerTr.appendChild(th);
51+
}
52+
// add select header special
53+
let selectTh = document.createElement('th');
54+
selectTh.innerText = 'Select';
55+
headerTr.appendChild(selectTh);
56+
table.append(headerTr);
57+
// populate results
58+
for (let x of slides) {
59+
let parent = document.createElement('tr');
60+
parent.setAttribute('data-id', x.id);
61+
parent.setAttribute('data-parent', 0);
62+
parent.setAttribute('data-level', 1);
63+
for (let z of headers) {
64+
let d = document.createElement('td');
65+
d.innerText = x[z] || '?';
66+
if (z==nameField) {
67+
d.setAttribute('data-column', 'name');
68+
}
69+
parent.appendChild(d);
70+
}
71+
// add special checkbox
72+
parentCheck = document.createElement('input');
73+
parentCheck.classList.add('form-check-input');
74+
parentCheck.type = 'checkbox';
75+
parentCheck.indeterminate = true; // cool!
76+
// TODO -- finish this. you'd want to add logic that sets this checkbox to true, false or indeterminate
77+
// depending on children selection. also select/deselect all children on change of this.
78+
// parent.appendChild(parentCheck);
79+
table.appendChild(parent);
80+
for (let y of results[x.id]) {
81+
console.log(x.raw);
82+
let child = document.createElement('tr');
83+
child.setAttribute('data-id', x.id+'-'+y.id);
84+
child.setAttribute('data-parent', x.id);
85+
child.setAttribute('data-level', 2);
86+
for (let z of headers) {
87+
let d = document.createElement('td');
88+
d.innerText = y[z] || '?';
89+
if (z==nameField) {
90+
d.setAttribute('data-column', 'name');
91+
}
92+
child.appendChild(d);
93+
}
94+
// special checkbox
95+
childCheck = document.createElement('input');
96+
childCheck.type = 'checkbox';
97+
childCheck.classList.add('form-check-input');
98+
childCheck.classList.add('result');
99+
childCheck.setAttribute('data-target', x.id);
100+
childCheck.setAttribute('data-self', y.id);
101+
childCheck.setAttribute('data-slideInfo', JSON.stringify(x.raw));
102+
childCheck.setAttribute('data-type', y.type);
103+
childCheck.checked = true;
104+
child.appendChild(childCheck);
105+
table.appendChild(child);
106+
}
107+
}
108+
t.appendChild(table);
109+
document.getElementById('output').appendChild(t);
110+
makeTreeTable('tree-table');
111+
}
112+
113+
async function downloadResults() {
114+
let checks = document.querySelectorAll('.result:checked');
115+
let marks = [];
116+
let heatmaps = [];
117+
for (let c of checks) {
118+
console.log(c.dataset);
119+
let parentSlide = JSON.parse(checks[0].dataset.slideinfo);
120+
if (c.dataset.type == 'human mark' || c.dataset.type == 'human mark') {
121+
let mark = await store.getMarkByIds([c.dataset.self], c.dataset.target);
122+
for (m of mark) {
123+
m.provenance.image = parentSlide;
124+
marks.push(m);
125+
}
126+
console.log(mark);
127+
} else if (c.dataset.type == 'heatmap' ) {
128+
let hm = await store.getHeatmap(c.dataset.parent, c.dataset.target);
129+
for (h of hm) {
130+
h.provenance.image = parentSlide;
131+
heatmaps.push(h);
132+
}
133+
}
134+
}
135+
console.log(marks, heatmaps);
136+
if (marks.length) {
137+
var element = document.createElement('a');
138+
element.setAttribute('href', 'data:application/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(marks)));
139+
element.setAttribute('download', 'camic_export_marks.json');
140+
element.style.display = 'none';
141+
document.body.appendChild(element);
142+
element.click();
143+
document.body.removeChild(element);
144+
}
145+
if (heatmaps.length) {
146+
var element = document.createElement('a');
147+
element.setAttribute('href', 'data:application/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(heatmaps)));
148+
element.setAttribute('download', 'camic_export_heatmaps.json');
149+
element.style.display = 'none';
150+
document.body.appendChild(element);
151+
element.click();
152+
document.body.removeChild(element);
153+
}
154+
// tell the user that data is missing
155+
if (!heatmaps.length && !marks.length) {
156+
alert('No data selected for download.');
157+
}
158+
}

apps/port/index.html renamed to apps/port/import.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<html>
33
<head>
44
<script src="../../core/Store.js"></script>
5-
<script src="./port.js"></script>
5+
<script src="./import.js"></script>
66
<script src="./xml2geo.js"></script>
77
<link rel="stylesheet" href="./style.css">
88
<title>Import and Export [caMicroscope]</title>
File renamed without changes.

apps/port/tree_table.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
function makeTreeTable(id) {
2+
var
3+
$table = $('#' + id);
4+
var rows = $table.find('tr');
5+
6+
rows.each(function(index, row) {
7+
var
8+
$row = $(row);
9+
var level = $row.data('level');
10+
var id = $row.data('id');
11+
var $columnName = $row.find('td[data-column="name"]');
12+
var children = $table.find('tr[data-parent="' + id + '"]');
13+
14+
if (children.length) {
15+
var expander = $columnName.prepend('' +
16+
'<span class="treegrid-expander glyphicon glyphicon-chevron-right">&#9660;</span>' +
17+
'');
18+
19+
children.hide();
20+
21+
expander.on('click', function(e) {
22+
var $target = $(e.target);
23+
if ($target.hasClass('glyphicon-chevron-right')) {
24+
$target
25+
.removeClass('glyphicon-chevron-right')
26+
.addClass('glyphicon-chevron-down');
27+
28+
children.show();
29+
} else {
30+
$target
31+
.removeClass('glyphicon-chevron-down')
32+
.addClass('glyphicon-chevron-right');
33+
34+
reverseHide($table, $row);
35+
}
36+
});
37+
}
38+
39+
$columnName.prepend('' +
40+
'<span class="treegrid-indent" style="width:' + 25 * level + 'px"></span>' +
41+
'');
42+
});
43+
44+
// Reverse hide all elements
45+
reverseHide = function(table, element) {
46+
var
47+
$element = $(element);
48+
var id = $element.data('id');
49+
var children = table.find('tr[data-parent="' + id + '"]');
50+
51+
if (children.length) {
52+
children.each(function(i, e) {
53+
reverseHide(table, e);
54+
});
55+
56+
$element
57+
.find('.glyphicon-chevron-down')
58+
.removeClass('glyphicon-chevron-down')
59+
.addClass('glyphicon-chevron-right');
60+
61+
children.hide();
62+
}
63+
};
64+
}

apps/table.html

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@
6464
<li class="nav-item link">
6565
<a class="nav-link" href="./dev-workbench/workbench.html"> <i class="fas fa-pencil-ruler"></i> Workbench</a>
6666
</li>
67+
<li class="nav-item link">
68+
<a class="nav-link" href="./port/export.html"> <i class="fas fa-solid fa-file-export"></i> Export</a>
69+
</li>
6770
<li class="nav-item link" style="font-family: sans-serif;">
6871
<a class="nav-link" href="./signup/signup.html"> <i class="fas fa-user-plus"></i> Signup</a>
6972
</li>
@@ -136,7 +139,7 @@ <h1 class="h1">caMicroscope</h1>
136139
onclick="(()=>{location.reload();return false;})()"> <i class="fas fa-sync-alt"></i>
137140
Reload</button>
138141
</div>
139-
<div>
142+
<div>
140143
<div id="slideUploadButton" class="btn-group float-right pl-md-2 pt-2">
141144
<button type="button" class="btn btn-primary " data-bs-toggle="modal"
142145
data-bs-target="#upload-dialog" onclick="hidePostButton(); hideCheckButton(); resetUploadForm();" > <i class="fas fa-upload"></i> Upload</button>
@@ -149,12 +152,12 @@ <h1 class="h1">caMicroscope</h1>
149152
</div>
150153
</div>
151154
</div>
152-
155+
153156

154157
</div>
155158
</div>
156159
</div>
157-
160+
158161

159162

160163
<div class="modal fade" id="upload-dialog" tabindex="-1" role="dialog"
@@ -285,7 +288,7 @@ <h5 class="modal-title" id="slideNameChangeModalLabel">Slide name change confirm
285288
</div>
286289
</div>
287290
</div>
288-
<!-- popup -->
291+
<!-- popup -->
289292
<div id="popup-container"></div>
290293
</div>
291294

@@ -295,7 +298,7 @@ <h5 class="modal-title" id="slideNameChangeModalLabel">Slide name change confirm
295298
<footer class="text-center text-white bg-dark p-3">
296299
<p class="p">Copyright © 2021 caMicroscope</p>
297300
</footer>
298-
301+
299302
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-p34f1UUtsS3wqzfto5wAAmdvj+osOnFyQFpp4Ua3gs/ZVWx6oOypYoCJhGGScy+8" crossorigin="anonymous"></script>
300303
<script src="./table.js"></script>
301304
</body>

apps/viewer/init.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ function initCore() {
167167
if ($D.params.retry) {
168168
$UI.message.addError(e.message);
169169
// can't reach Slide and return to home page
170-
if (e.isServiceError) redirect($D.pages.table, e.message, 0);
170+
if (e.isServiceError) redirect($D.pages.table, e.message, 1);
171171
} else {
172172
// If this is our first attempt, try one more time.
173173
let params = new URLSearchParams(window.location.search);

common/PathdbMods.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
function PathDbMods() {
1+
async function PathDbMods() {
22
console.log("PathDbMods()...");
33
// determine if user is authenicated
4-
fetch("/user/login_status?_format=json", {credentials: 'include'})
4+
await fetch("/user/login_status?_format=json", {credentials: 'include'})
55
.then(response => response.json())
66
.then(function(data) {
77
if (data!=0) {
@@ -11,6 +11,7 @@ function PathDbMods() {
1111
console.log(x)
1212
if (x.hasOwnProperty('token') && x.token) {
1313
document.cookie = "token=" + x.token + ";"
14+
console.log("set cookie, is now:" , document.cookie)
1415
}
1516
})
1617
} else {
@@ -49,9 +50,9 @@ function PathDbMods() {
4950
x.mpp = x.mpp_x
5051
}
5152
if (data.field_iip_path && data.field_iip_path.length >= 1) {
52-
x.location = data.field_iip_path[0].value;
53-
//x.location = "pathdb*" + pathdbid;
54-
x.url = "../../img/IIP/raw/?DeepZoom=" + x.location + ".dzi";
53+
//x.location = data.field_iip_path[0].value;
54+
x.location = "pathdb*" + pathdbid;
55+
x.url = "../../img/IIP/raw/?DeepZoom=pathdb*" + pathdbid + ".dzi";
5556
} else {
5657
throw "no iip path in pathdb data"
5758
}

common/dynamicLoadScript.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ const params = getUrlVars();
66
let file;
77
if (params.mode && params.mode==="pathdb"){
88
ImgloaderMode = 'iip';
9-
PathDbMods()
9+
PathDbMods().then(x=>{IsPackageLoading = true})
1010
}
1111
else if(params.slideId&&params.id&&params.slideId==="local"&&params.id.includes('http://localhost:8888')){
1212
ImgloaderMode = 'imgbox';
1313
NanoBorbMods()
1414
init_LocalStore()
15+
IsPackageLoading = true;
1516
}else{
1617
ImgloaderMode = 'iip';
18+
IsPackageLoading = true;
1719
// no mods to perform as of now
1820
}
19-
IsPackageLoading=true;

0 commit comments

Comments
 (0)