Skip to content

Commit

Permalink
Show auxiliary images and metadata on item pages.
Browse files Browse the repository at this point in the history
  • Loading branch information
manthey committed Jun 12, 2020
1 parent 712ce6d commit a6a6397
Show file tree
Hide file tree
Showing 14 changed files with 275 additions and 41 deletions.
3 changes: 3 additions & 0 deletions girder/girder_large_image/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,9 @@ def validateBoolean(doc):
constants.PluginSettings.LARGE_IMAGE_SHOW_EXTRA_PUBLIC,
constants.PluginSettings.LARGE_IMAGE_SHOW_EXTRA,
constants.PluginSettings.LARGE_IMAGE_SHOW_EXTRA_ADMIN,
constants.PluginSettings.LARGE_IMAGE_SHOW_ITEM_EXTRA_PUBLIC,
constants.PluginSettings.LARGE_IMAGE_SHOW_ITEM_EXTRA,
constants.PluginSettings.LARGE_IMAGE_SHOW_ITEM_EXTRA_ADMIN,
})
def validateDictOrJSON(doc):
val = doc['value']
Expand Down
3 changes: 3 additions & 0 deletions girder/girder_large_image/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ class PluginSettings(object):
LARGE_IMAGE_SHOW_EXTRA_PUBLIC = 'large_image.show_extra_public'
LARGE_IMAGE_SHOW_EXTRA = 'large_image.show_extra'
LARGE_IMAGE_SHOW_EXTRA_ADMIN = 'large_image.show_extra_admin'
LARGE_IMAGE_SHOW_ITEM_EXTRA_PUBLIC = 'large_image.show_item_extra_public'
LARGE_IMAGE_SHOW_ITEM_EXTRA = 'large_image.show_item_extra'
LARGE_IMAGE_SHOW_ITEM_EXTRA_ADMIN = 'large_image.show_item_extra_admin'
LARGE_IMAGE_SHOW_VIEWER = 'large_image.show_viewer'
LARGE_IMAGE_DEFAULT_VIEWER = 'large_image.default_viewer'
LARGE_IMAGE_AUTO_SET = 'large_image.auto_set'
Expand Down
1 change: 1 addition & 0 deletions girder/girder_large_image/web_client/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { registerPluginNamespace } from '@girder/core/pluginUtils';
import './routes';
import './views/fileList';
import './views/itemList';
import './views/itemView';
import './views/imageViewerSelectWidget';

// expose symbols under girder.plugins
Expand Down
3 changes: 2 additions & 1 deletion girder/girder_large_image/web_client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
"dependencies": {
"copy-webpack-plugin": "^4.5.2",
"d3": "^3.5.16",
"hammerjs": "^2.0.8",
"geojs": "^0.20.0",
"hammerjs": "^2.0.8",
"js-yaml": "^3.14.0",
"sinon": "^2.1.0",
"slideatlas-viewer": "4.0.3"
},
Expand Down
22 changes: 22 additions & 0 deletions girder/girder_large_image/web_client/stylesheets/itemView.styl
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.g-item-view-large-image
margin-top 20px

.li-metadata-tabs
margin-top 10px
.yaml
display block
unicode-bidi embed
font-family monospace
white-space pre-wrap
td
max-width 50vw
td
max-width 33vw

a.g-widget-auximage
display inline-block
padding 5px
.g-widget-auximage-title
color black
font-weight bold
font-size 1.2em
81 changes: 81 additions & 0 deletions girder/girder_large_image/web_client/templates/itemView.pug
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
mixin maketable(info, depth)
table.table.table-hover.table-condensed
thead
th Property
th Value
each value, key in info
tr
if Array.isArray(value) && value.length <= 100
td(scope="row", key=key, rowspan=value.length || 1) #{key}
for rowvalue, rowidx in value
if rowidx
tr
+tableentry(rowvalue, depth)
else
+tableentry(rowvalue, depth)
else
td(scope="row", key=key) #{key}
+tableentry(value, depth)

mixin tableentry(value, depth)
//- each value, if an array or object, convert to yaml or json
and add a class to show it differently
if value === null
td.null &lt;null&gt;
else if value && value.constructor.name === 'Object' && (depth || 0) < 3
td.subtable
+maketable(value, (depth || 0) + 1)
else if Array.isArray(value) || (value && value.constructor.name === 'Object')
if yaml.dump(value).split('\n').length <= 100
td.yaml #{yaml.dump(value)}
else
td.json #{JSON.stringify(value)}
else
td #{value}

//- check what metadata we have that we want to list
- var metadataList = [];
if Array.isArray(extra.metadata)
for mkey in extra.metadata
if largeImageMetadata[mkey]
- metadataList.push(mkey);

if metadataList.length
.g-widget-metadata-header
i.icon-tags
| Large Image Metadata
.g-widget-metadata-container.li-metadata-tabs
ul.nav.nav-tabs(role="tablist")
for mkey, midx in metadataList
- title = mkey.substr(0, 1).toUpperCase() + mkey.substr(1);
li(role="presentation", class=midx ? "" : "active")
a(href="#li-metadata-" + mkey, role="tab", data-toggle="tab") #{title}
.tab-content
for mkey, midx in metadataList
.tab-pane(id="li-metadata-" + mkey, role="tabpanel", class=midx ? "" : "active")
+maketable(largeImageMetadata[mkey])

//- check what images we have that we want to list
- var imageList = []
if Array.isArray(extra.images)
for ikey in extra.images
if ikey === '*'
for likey in largeImageMetadata.images
if imageList.indexOf(likey) < 0
- imageList.push(likey);
else if largeImageMetadata.images.indexOf(ikey) >= 0 && imageList.indexOf(ikey) < 0
- imageList.push(ikey);
- var imageWidth = 400, imageHeight = 400;

if imageList.length
.g-widget-metadata-header.auximage
i.icon-picture
| Associated Images
.g-widget-metadata-container.auximage
for ikey in imageList
- title = ikey.substr(0, 1).toUpperCase() + ikey.substr(1);
a.g-widget-auximage(href=`${imageUrl}${ikey}`, target="_blank")
.g-widget-auximage-title
| #{title}
.g-widget-auximage-image
img(src=`${imageUrl}${ikey}?width=${imageWidth}&height=${imageHeight}`)
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,39 @@ form#g-large-image-form(role="form")
input.g-large-image-thumbnails-hide(type="radio", name="g-large-image-thumbnails", checked=settings['large_image.show_thumbnails'] !== false ? undefined : 'checked')
| Don't show
.form-group
- var detailplaceholder = 'A JSON object listing extra details to show. For example: {"images": ["label", "macro"]}'
- var detailtitle = 'This can be specified images and metadata as a JSON object such as {"images": ["label", "macro"]}'
label
| Additional details to show in item lists
p.g-large-image-description
| Details to show to anonymous users.
| Details to show in lists to anonymous users.
input.input-sm.form-control.g-large-image-show-extra-public(
type="text", value=settings['large_image.show_extra_public'], placeholder='A JSON object listing extra details to show. For example: {"images": ["label", "macro"]}', title='This can be specified images and metadata as a JSON object such as {"images": ["label", "macro"]}')
type="text", value=settings['large_image.show_extra_public'], placeholder=detailplaceholder, title=detailtitle)
p.g-large-image-description
| Details to show for all logged-in users.
| Details to show in lists for all logged-in users.
input.input-sm.form-control.g-large-image-show-extra(
type="text", value=settings['large_image.show_extra'], placeholder='A JSON object listing extra details to show. For example: {"images": ["label", "macro"]}', title='This can be specified images and metadata as a JSON object such as {"images": ["label", "macro"]}')
type="text", value=settings['large_image.show_extra'], placeholder=detailplaceholder, title=detailtitle)
p.g-large-image-description
| Details to show for admins and owners of the images.
| Details to show is lists for admins and owners of the images.
input.input-sm.form-control.g-large-image-show-extra-admin(
type="text", value=settings['large_image.show_extra_admin'], placeholder='A JSON object listing extra details to show. For example: {"images": ["label", "macro"]}', title='This can be specified images and metadata as a JSON object such as {"images": ["label", "macro"]}')
type="text", value=settings['large_image.show_extra_admin'], placeholder=detailplaceholder, title=detailtitle)
.form-group
- var detailplaceholder = 'A JSON object listing extra details to show. For example: {"metadata": ["tile", "internal"], "images": ["label", "macro", "*"]}'
- var detailtitle = 'This can be specified images and metadata as a JSON object such as {"metadata": ["tile", "internal"], "images": ["label", "macro", "*"]}'
label
| Additional details to show in item pages
p.g-large-image-description
| Details to show on item pages to anonymous users.
input.input-sm.form-control.g-large-image-show-item-extra-public(
type="text", value=settings['large_image.show_item_extra_public'], placeholder=detailplaceholder, title=detailtitle)
p.g-large-image-description
| Details to show on item pages for all logged-in users.
input.input-sm.form-control.g-large-image-show-item-extra(
type="text", value=settings['large_image.show_item_extra'], placeholder=detailplaceholder, title=detailtitle)
p.g-large-image-description
| Details to show on item pages for admins and owners of the images.
input.input-sm.form-control.g-large-image-show-item-extra-admin(
type="text", value=settings['large_image.show_item_extra_admin'], placeholder=detailplaceholder, title=detailtitle)
.form-group
label
| Maximum number of thumbnail files to save per item
Expand Down
33 changes: 33 additions & 0 deletions girder/girder_large_image/web_client/views/configView.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import View from '@girder/core/views/View';

import PluginConfigBreadcrumbWidget from '@girder/core/views/widgets/PluginConfigBreadcrumbWidget';
import { restRequest } from '@girder/core/rest';
import { AccessType } from '@girder/core/constants';
import events from '@girder/core/events';

import ConfigViewTemplate from '../templates/largeImageConfig.pug';
Expand Down Expand Up @@ -42,6 +43,15 @@ var ConfigView = View.extend({
}, {
key: 'large_image.show_extra_admin',
value: this.$('.g-large-image-show-extra-admin').val()
}, {
key: 'large_image.show_item_extra_public',
value: this.$('.g-large-image-show-item-extra-public').val()
}, {
key: 'large_image.show_item_extra',
value: this.$('.g-large-image-show-item-extra').val()
}, {
key: 'large_image.show_item_extra_admin',
value: this.$('.g-large-image-show-item-extra-admin').val()
}]);
}
},
Expand Down Expand Up @@ -135,6 +145,29 @@ var ConfigView = View.extend({
type: 'GET',
url: 'large_image/settings'
}).done((resp) => {
resp.extraInfo = {};
resp.extraItemInfo = {};
let extraList = [{
access: null,
extraInfo: 'large_image.show_extra_public',
extraItemInfo: 'large_image.show_item_extra_public'
}, {
access: AccessType.READ,
extraInfo: 'large_image.show_extra',
extraItemInfo: 'large_image.show_item_extra'
}, {
access: AccessType.ADMIN,
extraInfo: 'large_image.show_extra_admin',
extraItemInfo: 'large_image.show_item_extra_admin'
}];
extraList.forEach((entry) => {
['extraInfo', 'extraItemInfo'].forEach((key) => {
try {
resp[key][entry.access] = JSON.parse(resp[entry[key]]);
} catch (err) {
}
});
});
ConfigView.settings = resp;
if (callback) {
callback(ConfigView.settings);
Expand Down
22 changes: 1 addition & 21 deletions girder/girder_large_image/web_client/views/itemList.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,26 +89,6 @@ wrap(ItemListWidget, 'render', function (render) {
largeImageConfig.getSettings((settings) => {
// we will want to also show metadata, so these entries might look like
// {images: ['label', 'macro'], meta: [{key: 'abc', label: 'ABC'}]}
var extraInfo = {};
if (settings['large_image.show_extra_public']) {
try {
extraInfo[null] = JSON.parse(settings['large_image.show_extra_public']);
} catch (err) {
}
}
if (settings['large_image.show_extra']) {
try {
extraInfo[AccessType.READ] = JSON.parse(settings['large_image.show_extra']);
} catch (err) {
}
}
if (settings['large_image.show_extra_admin']) {
try {
extraInfo[AccessType.ADMIN] = JSON.parse(settings['large_image.show_extra_admin']);
} catch (err) {
}
}

if (settings['large_image.show_thumbnails'] === false ||
this.$('.large_image_container').length > 0) {
return this;
Expand All @@ -123,7 +103,7 @@ wrap(ItemListWidget, 'render', function (render) {
var elem = $('<div class="large_image_container"/>');
if (item.get('largeImage')) {
item.getAccessLevel(function () {
addLargeImageDetails(item, elem, parent, extraInfo);
addLargeImageDetails(item, elem, parent, settings.extraInfo);
});
}
$('a[g-item-cid="' + item.cid + '"]>i', parent).before(elem);
Expand Down
73 changes: 73 additions & 0 deletions girder/girder_large_image/web_client/views/itemView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import $ from 'jquery';
import yaml from 'js-yaml';
import { AccessType } from '@girder/core/constants';
import { restRequest, getApiRoot } from '@girder/core/rest';
import { wrap } from '@girder/core/utilities/PluginUtils';
import ItemView from '@girder/core/views/body/ItemView';
import View from '@girder/core/views/View';

import largeImageConfig from './configView';
import itemViewWidget from '../templates/itemView.pug';
import '../stylesheets/itemView.styl';

wrap(ItemView, 'render', function (render) {
// ItemView is a special case in which rendering is done asynchronously,
// so we must listen for a render event.
this.once('g:rendered', function () {
if (this.model.get('largeImage') && this.model.get('largeImage').fileId) {
largeImageConfig.getSettings((settings) => {
var access = this.model.getAccessLevel();
var extra = settings.extraItemInfo[access] || settings.extraItemInfo[AccessType.READ] || {};
var largeImageMetadata = {};
var promises = [];
var needed = {
tile: extra.metadata && extra.metadata.indexOf('tile') >= 0 ? '' : undefined,
internal: extra.metadata && extra.metadata.indexOf('internal') >= 0 ? '/internal_metadata' : undefined,
images: extra.images && extra.images.length ? '/images' : undefined
};
Object.entries(needed).forEach(([key, url]) => {
if (url !== undefined) {
promises.push(restRequest({
url: `item/${this.model.id}/tiles${url}`,
error: null
}).done((resp) => {
largeImageMetadata[key] = resp;
}));
}
});
$.when.apply($, promises).then(() => {
this.itemViewWidget = new ItemViewWidget({
el: $('<div>', {class: 'g-item-view-large-image'})
.insertAfter(this.$('.g-item-metadata')),
parentView: this,
imageModel: this.model,
extra: extra,
metadata: largeImageMetadata
});
this.itemViewWidget.render();
return null;
});
});
}
}, this);
render.call(this);
});

var ItemViewWidget = View.extend({
initialize: function (settings) {
this.itemId = settings.imageModel.id;
this.model = settings.imageModel;
this.extra = settings.extra;
this.metadata = settings.metadata;
},

render: function () {
this.$el.html(itemViewWidget({
extra: this.extra,
largeImageMetadata: this.metadata,
yaml: yaml,
imageUrl: `${getApiRoot()}/item/${this.itemId}/tiles/images/`
}));
return this;
}
});
9 changes: 8 additions & 1 deletion girder/test_girder/test_large_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,11 @@ def testSettings(server):
testExtraVal = json.dumps({'images': ['label']})
for key in (constants.PluginSettings.LARGE_IMAGE_SHOW_EXTRA_PUBLIC,
constants.PluginSettings.LARGE_IMAGE_SHOW_EXTRA,
constants.PluginSettings.LARGE_IMAGE_SHOW_EXTRA_ADMIN):
constants.PluginSettings.LARGE_IMAGE_SHOW_EXTRA_ADMIN,
constants.PluginSettings.LARGE_IMAGE_SHOW_ITEM_EXTRA_PUBLIC,
constants.PluginSettings.LARGE_IMAGE_SHOW_ITEM_EXTRA,
constants.PluginSettings.LARGE_IMAGE_SHOW_ITEM_EXTRA_ADMIN,
):
Setting().set(key, '')
assert Setting().get(key) == ''
Setting().set(key, testExtraVal)
Expand Down Expand Up @@ -102,6 +106,9 @@ def testSettings(server):
assert settings[constants.PluginSettings.LARGE_IMAGE_SHOW_EXTRA_PUBLIC] == testExtraVal
assert settings[constants.PluginSettings.LARGE_IMAGE_SHOW_EXTRA] == testExtraVal
assert settings[constants.PluginSettings.LARGE_IMAGE_SHOW_EXTRA_ADMIN] == testExtraVal
assert settings[constants.PluginSettings.LARGE_IMAGE_SHOW_ITEM_EXTRA_PUBLIC] == testExtraVal
assert settings[constants.PluginSettings.LARGE_IMAGE_SHOW_ITEM_EXTRA] == testExtraVal
assert settings[constants.PluginSettings.LARGE_IMAGE_SHOW_ITEM_EXTRA_ADMIN] == testExtraVal
assert settings[constants.PluginSettings.LARGE_IMAGE_AUTO_SET] is True
assert settings[constants.PluginSettings.LARGE_IMAGE_MAX_THUMBNAIL_FILES] == 5
assert settings[constants.PluginSettings.LARGE_IMAGE_MAX_SMALL_IMAGE_SIZE] == 1024
Expand Down
Loading

0 comments on commit a6a6397

Please sign in to comment.