Skip to content

Commit

Permalink
Merge pull request #241 from DigitalSlideArchive/annotation-metadata
Browse files Browse the repository at this point in the history
Support annotation metadata
  • Loading branch information
manthey authored Sep 15, 2022
2 parents 9429f16 + e38076a commit 79cf689
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 25 deletions.
37 changes: 33 additions & 4 deletions histomicsui/web_client/dialogs/saveAnnotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import _ from 'underscore';
import $ from 'jquery';
import tinycolor from 'tinycolor2';

import AccessWidget from '@girder/core/views/widgets/AccessWidget';
import View from '@girder/core/views/View';
import { AccessType } from '@girder/core/constants';
import { formatDate, DATE_SECOND } from '@girder/core/misc';
import AccessWidget from '@girder/core/views/widgets/AccessWidget';
// import MetadataWidget from '@girder/core/views/widgets/MetadataWidget';
import View from '@girder/core/views/View';

import saveAnnotation from '../templates/dialogs/saveAnnotation.pug';
import MetadataWidget from '../panels/MetadataWidget';
import '../stylesheets/dialogs/saveAnnotation.styl';
import saveAnnotation from '../templates/dialogs/saveAnnotation.pug';

/**
* Create a modal dialog with fields to edit the properties of
Expand Down Expand Up @@ -40,7 +42,6 @@ var SaveAnnotation = View.extend({

if (showStyleEditor) {
const elements = this.annotation.get('annotation').elements;
console.assert(elements.length > 0); // otherwise we wouldn't show the style editor
const firstElement = elements[0];
if (elements.every((d) => d.lineWidth === firstElement.lineWidth)) {
defaultStyles.lineWidth = firstElement.lineWidth;
Expand All @@ -66,6 +67,28 @@ var SaveAnnotation = View.extend({
})
).girderModal(this);
this.$('.h-colorpicker').colorpicker();

if (this.annotation.id) {
if (!this.annotation.meta) {
this.annotation._meta = Object.assign({}, (this.annotation.get('annotation') || {}).attributes || {});
}
// copy the metadata to a place that is expected for the widget
if (!this.metadataWidget) {
this.metadataWidget = new MetadataWidget({
item: this.annotation,
parentView: this,
fieldName: '_meta',
accessLevel: this.annotation.get('_accessLevel'),
panel: false,
noSave: true
});
}
this.metadataWidget.setItem(this.annotation);
this.metadataWidget.accessLevel = this.annotation.get('_accessLevel');
this.metadataWidget.setElement(this.$('.hui-annotation-metadata')).render();
}

this.$el.find('.modal-dialog').addClass('hui-save-annotation-dialog');
return this;
},

Expand All @@ -85,6 +108,9 @@ var SaveAnnotation = View.extend({
},

cancel(evt) {
if (this.annotation) {
delete this.annotation._meta;
}
evt.preventDefault();
this.$el.modal('hide');
},
Expand Down Expand Up @@ -144,6 +170,8 @@ var SaveAnnotation = View.extend({
name: this.$('#h-annotation-name').val(),
description: this.$('#h-annotation-description').val()
});
this.annotation.attributes.annotation.attributes = this.annotation._meta;
delete this.annotation._meta;
this.trigger('g:submit');
this.$el.modal('hide');
},
Expand Down Expand Up @@ -171,6 +199,7 @@ var dialog = new SaveAnnotation({
*/
function show(annotation, options) {
_.defaults(options, { title: 'Create annotation' });
delete annotation._meta;
dialog.annotation = annotation;
dialog.options = options;
dialog.setElement('#g-dialog-container').render();
Expand Down
2 changes: 1 addition & 1 deletion histomicsui/web_client/panels/MetadataPlot.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ var MetadataPlot = Panel.extend({
},
'click .h-panel-maximize': function (event) {
this.$el.html('');
this.expand();
this.expand(event);
this.$('.s-panel-content').addClass('in');
let panelElem = this.$el.closest('.s-panel');
let maximize = !panelElem.hasClass('h-panel-maximized');
Expand Down
102 changes: 86 additions & 16 deletions histomicsui/web_client/panels/MetadataWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { AccessType } from '@girder/core/constants';
import { confirm } from '@girder/core/dialog';
import events from '@girder/core/events';
import { localeSort } from '@girder/core/misc';
import View from '@girder/core/views/View';

import JsonMetadatumEditWidgetTemplate from '@girder/core/templates/widgets/jsonMetadatumEditWidget.pug';
import JsonMetadatumViewTemplate from '@girder/core/templates/widgets/jsonMetadatumView.pug';
Expand All @@ -20,7 +21,21 @@ import 'bootstrap/js/dropdown';
import metadataWidgetTemplate from '../templates/panels/metadataWidget.pug';
import '../stylesheets/panels/metadataWidget.styl';

var MetadatumWidget = Panel.extend({
function getMetadataRecord(item, fieldName) {
if (item[fieldName]) {
return item[fieldName];
}
let meta = item.attributes;
fieldName.split('.').forEach((part) => {
if (!meta[part]) {
meta[part] = {};
}
meta = meta[part];
});
return meta;
}

var MetadatumWidget = View.extend({
className: 'g-widget-metadata-row',

events: {
Expand All @@ -38,6 +53,7 @@ var MetadatumWidget = Panel.extend({
this.parentView = settings.parentView;
this.fieldName = settings.fieldName;
this.apiPath = settings.apiPath;
this.noSave = settings.noSave;
this.onMetadataEdited = settings.onMetadataEdited;
this.onMetadataAdded = settings.onMetadataAdded;
},
Expand All @@ -46,8 +62,8 @@ var MetadatumWidget = Panel.extend({
var newMode = this.parentView.modes[to];

if (_.has(newMode, 'validation') &&
_.has(newMode.validation, 'from') &&
_.has(newMode.validation.from, from)) {
_.has(newMode.validation, 'from') &&
_.has(newMode.validation.from, from)) {
var validate = newMode.validation.from[from][0];
var msg = newMode.validation.from[from][1];

Expand Down Expand Up @@ -85,6 +101,7 @@ var MetadatumWidget = Panel.extend({
parentView: this,
fieldName: this.fieldName,
apiPath: this.apiPath,
noSave: this.noSave,
onMetadataEdited: this.onMetadataEdited,
onMetadataAdded: this.onMetadataAdded
}, overrides || {});
Expand All @@ -105,6 +122,7 @@ var MetadatumWidget = Panel.extend({
parentView: this,
fieldName: this.fieldName,
apiPath: this.apiPath,
noSave: this.noSave,
onMetadataEdited: this.onMetadataEdited,
onMetadataAdded: this.onMetadataAdded
};
Expand Down Expand Up @@ -142,7 +160,7 @@ var MetadatumWidget = Panel.extend({
}
});

var MetadatumEditWidget = Panel.extend({
var MetadatumEditWidget = View.extend({
events: {
'click .g-widget-metadata-cancel-button': 'cancelEdit',
'click .g-widget-metadata-save-button': 'save',
Expand All @@ -161,6 +179,7 @@ var MetadatumEditWidget = Panel.extend({
key: this.$el.find('.g-widget-metadata-key-input').val(),
value: this.getCurrentValue()
});
return false;
}
},

Expand All @@ -173,6 +192,7 @@ var MetadatumEditWidget = Panel.extend({
this.newDatum = settings.newDatum;
this.fieldName = settings.fieldName;
this.apiPath = settings.apiPath;
this.noSave = settings.noSave;
this.onMetadataEdited = settings.onMetadataEdited;
this.onMetadataAdded = settings.onMetadataAdded;
},
Expand All @@ -187,6 +207,11 @@ var MetadatumEditWidget = Panel.extend({
event.stopImmediatePropagation();
const target = $(event.currentTarget);
var metadataList = target.parent().parent();
if (this.noSave) {
delete getMetadataRecord(this.item, this.fieldName)[this.key];
metadataList.remove();
return;
}
var params = {
text: 'Are you sure you want to delete the metadatum <b>' +
_.escape(this.key) + '</b>?',
Expand Down Expand Up @@ -222,14 +247,14 @@ var MetadatumEditWidget = Panel.extend({
event.stopImmediatePropagation();
const target = $(event.currentTarget);
var curRow = target.parent(),
tempKey = curRow.find('.g-widget-metadata-key-input').val(),
tempKey = curRow.find('.g-widget-metadata-key-input').val().trim(),
tempValue = (value !== undefined) ? value : curRow.find('.g-widget-metadata-value-input').val();
if (this.newDatum && tempKey === '') {
events.trigger('g:alert', {
text: 'A key is required for all metadata.',
type: 'warning'
});
return;
return false;
}
var saveCallback = () => {
this.key = tempKey;
Expand Down Expand Up @@ -260,6 +285,18 @@ var MetadatumEditWidget = Panel.extend({
if (this.onMetadataAdded) {
this.onMetadataAdded(tempKey, tempValue, saveCallback, errorCallback);
} else {
if (this.noSave) {
if (getMetadataRecord(this.item, this.fieldName)[tempKey] !== undefined) {
events.trigger('g:alert', {
text: tempKey + ' is already a metadata key',
type: 'warning'
});
return false;
}
getMetadataRecord(this.item, this.fieldName)[tempKey] = tempValue;
this.parentView.parentView.render();
return;
}
this.item.addMetadata(tempKey, tempValue, saveCallback, errorCallback, {
field: this.fieldName,
path: this.apiPath
Expand All @@ -269,6 +306,20 @@ var MetadatumEditWidget = Panel.extend({
if (this.onMetadataEdited) {
this.onMetadataEdited(tempKey, this.key, tempValue, saveCallback, errorCallback);
} else {
if (this.noSave) {
tempKey = tempKey === '' ? this.key : tempKey;
if (tempKey !== this.key && getMetadataRecord(this.item, this.fieldName)[tempKey] !== undefined) {
events.trigger('g:alert', {
text: tempKey + ' is already a metadata key',
type: 'warning'
});
return false;
}
delete getMetadataRecord(this.item, this.fieldName)[this.key];
getMetadataRecord(this.item, this.fieldName)[tempKey] = tempValue;
this.parentView.parentView.render();
return;
}
this.item.editMetadata(tempKey, this.key, tempValue, saveCallback, errorCallback, {
field: this.fieldName,
path: this.apiPath
Expand Down Expand Up @@ -307,6 +358,7 @@ var JsonMetadatumEditWidget = MetadatumEditWidget.extend({
text: 'The field contains invalid JSON and can not be saved.',
type: 'warning'
});
return false;
}
},

Expand Down Expand Up @@ -345,7 +397,7 @@ var MetadataWidget = Panel.extend({
this.addMetadata(event, 'simple');
},
'click .h-panel-maximize': function (event) {
this.expand();
this.expand(event);
this.$('.s-panel-content').addClass('in');
let panelElem = this.$el.closest('.s-panel');
let maximize = !panelElem.hasClass('h-panel-maximized');
Expand Down Expand Up @@ -382,6 +434,8 @@ var MetadataWidget = Panel.extend({
this.apiPath = settings.apiPath;
this.accessLevel = settings.accessLevel;
this.onMetadataEdited = settings.onMetadataEdited;
this.panel = settings.panel === undefined ? true : settings.panel;
this.noSave = settings.noSave;
// the event is created
this.on('h-metadata-panel-update', (event) => {
this.renderMetadataWidgetHeader(event);
Expand Down Expand Up @@ -456,6 +510,7 @@ var MetadataWidget = Panel.extend({
apiPath: this.apiPath,
accessLevel: this.accessLevel,
parentView: this,
noSave: this.noSave,
onMetadataEdited: this.onMetadataEdited,
onMetadataAdded: this.onMetadataAdded
});
Expand All @@ -468,6 +523,7 @@ var MetadataWidget = Panel.extend({
fieldName: this.fieldName,
apiPath: this.apiPath,
accessLevel: this.accessLevel,
noSave: this.noSave,
newDatum: true,
parentView: widget,
onMetadataEdited: this.onMetadataEdited,
Expand All @@ -478,20 +534,32 @@ var MetadataWidget = Panel.extend({
},

renderMetadataWidgetHeader: function () {
// pervent automatically collapse the widget after rendering again
// prevent automatically collapsing the widget after rendering again
this.render();
},

render: function () {
if (this.item && this.item.id) {
const imageId = this.item.id;
const apiPath = `/item/${imageId}/metadata`;
this.item.getAccessLevel((accessLevel) => {
var metaDict = this.item.get(this.fieldName) || {};
let func = this.item.getAccessLevel;
if (this.item.get('_modelType') === 'annotation') {
func = (callback) => {
const accessLevel = this.item.getAccessLevel();
callback(accessLevel);
};
}
func.call(this.item, (accessLevel) => {
const fieldParts = this.fieldName.split('.');
let metaDict = this.item.get(fieldParts[0]) || {};
fieldParts.slice(1).forEach((part) => {
metaDict = metaDict[part] || {};
});
if (this.item[this.fieldName]) {
metaDict = this.item[this.fieldName];
}
var metaKeys = Object.keys(metaDict);
metaKeys.sort(localeSort);
var firstKey = (metaKeys)[0];
var firstValue = metaDict[firstKey];
const firstKey = (metaKeys)[0];
let firstValue = metaDict[firstKey];
if (_.isObject(firstValue)) {
// if the value is a json object, JSON.stringify to make it more readable
firstValue = JSON.stringify(firstValue);
Expand All @@ -503,8 +571,9 @@ var MetadataWidget = Panel.extend({
firstValue: firstValue,
accessLevel: this.item.attributes._accessLevel,
AccessType: AccessType,
panel: this.panel,
// if never rendered, the jquery selector will be empty and won't be visible
collapsed: !this.$('.s-panel-content').hasClass('in') && !this.$el.closest('.s-panel').hasClass('h-panel-maximized')
collapsed: this.panel && !this.$('.s-panel-content').hasClass('in') && !this.$el.closest('.s-panel').hasClass('h-panel-maximized')
}));
// Append each metadatum
_.each(metaKeys, function (metaKey) {
Expand All @@ -515,7 +584,8 @@ var MetadataWidget = Panel.extend({
accessLevel: this.item.attributes._accessLevel,
parentView: this,
fieldName: this.fieldName,
apiPath: apiPath,
apiPath: this.apiPath,
noSave: this.noSave,
onMetadataEdited: this.onMetadataEdited,
onMetadataAdded: this.onMetadataAdded
}).render().$el);
Expand Down
20 changes: 20 additions & 0 deletions histomicsui/web_client/stylesheets/dialogs/saveAnnotation.styl
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,23 @@
float left
.hui-info-list-entry
user-select text
.hui-annotation-metadata.hui-annotation-metadata-dialog
.s-panel-title-container
background-color #f0f0f0
padding 5px 6px
color #555
font-size 16px
font-weight bold
margin-top 8px
position relative
i.icon-up-open, i.icon-down-open
display none
.g-widget-metadata-add-button
margin-top 0
@media (min-width: 768px)
.modal-dialog.hui-save-annotation-dialog
width inherit
@media (min-width: 900px)
.modal-dialog.hui-save-annotation-dialog
width 70%
max-width 900px
3 changes: 2 additions & 1 deletion histomicsui/web_client/templates/dialogs/saveAnnotation.pug
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@
i.icon-share
| Unique ID: #{model.id}
if model.get('_version')
|
= " "
i.icon-angle-circled-down
| Global Version: #{model.get('_version')}
.hui-annotation-metadata.hui-annotation-metadata-dialog
if showStyleEditor
hr
h4 Reset the style for all point, line, and polygon elements in this annotation
Expand Down
Loading

0 comments on commit 79cf689

Please sign in to comment.