Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show auxiliary images and metadata on item pages. #457

Merged
merged 1 commit into from
Jun 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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