Skip to content

Commit

Permalink
Changes to make it easier to inherit the metadata widget
Browse files Browse the repository at this point in the history
  • Loading branch information
manthey committed Jun 12, 2023
1 parent 5a3659a commit e59f7a8
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Added an internal field to report populated tile levels in some sources ([#1197](../../pull/1197), [#1199](../../pull/1199))
- Allow specifying an empty style dict ([#1200](../../pull/1200))
- Allow rounding histogram bin edges and reducing bin counts ([#1201](../../pull/1201))
- Allow configuring which metadata can be added to items ([#1202](../../pull/1202))

### Changes
- Change how extensions and fallback priorities interact ([#1192](../../pull/1192))
Expand Down
43 changes: 43 additions & 0 deletions docs/girder_config_options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ The yaml file has the following structure:
.large_image_config.yaml
~~~~~~~~~~~~~~~~~~~~~~~~

Items Lists
...........

This is used to specify how items appear in item lists. There are two settings, one for folders in the main Girder UI and one for folders in dialogs (such as when browsing in the file dialog).

::
Expand Down Expand Up @@ -128,6 +131,46 @@ This is used to specify how items appear in item lists. There are two settings,

If there are no large images in a folder, none of the image columns will appear.

Item Metadata
.............

By default, item metadata can contain any keys and values. These can be given better titles and restricted in their data types.

::

---
# If present, offer to add these specific keys and restrict their datatypes
itemMetadata:
-
# value is the key name within the metadata
value: stain
# title is the displayed titles
title: Stain
# description is used as both a tooltip and as placeholder text
description: Staining method
# if required is true, the delete button does not appear
required: true
# If a regex is specified, the value must match
# regex: '^(Eosin|H&E|Other)$'
# If an enum is specified, the value is set via a dropdown select box
enum:
- Eosin
- H&E
- Other
# If a default is specified, when the value is created, it will show
# this value in the control
default: H&E
-
value: rating
# type can be "number", "integer", or "text" (default)
type: number
# minimum and maximum are inclusive
minimum: 0
maximum: 10
# Exclusive values can be specified instead
# exclusiveMinimum: 0
# exclusiveMaximum: 10

Editing Configuration Files
---------------------------

Expand Down
107 changes: 88 additions & 19 deletions girder/girder_large_image/web_client/views/metadataWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ var MetadatumWidget = View.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 All @@ -106,7 +106,7 @@ var MetadatumWidget = View.extend({
var fromEditorMode = (existingEditor instanceof JsonMetadatumEditWidget) ? 'json' : 'simple';
var newValue = (overrides || {}).value || existingEditor.$el.attr('g-value');
if (!this._validate(fromEditorMode, newEditorMode, newValue)) {
return;
return false;
}

var row = existingEditor.$el;
Expand Down Expand Up @@ -246,7 +246,7 @@ var MetadatumEditWidget = View.extend({
confirmCallback: () => {
this.item.removeMetadata(this.key, function () {
metadataList.remove();
// TODO: trigger an event?
this.parentView.parentView.trigger('li-metadata-widget-update', {});
}, null, {
field: this.fieldName,
path: this.apiPath
Expand Down Expand Up @@ -328,7 +328,8 @@ var MetadatumEditWidget = View.extend({
} else {
this.parentView.mode = 'simple';
}
// TODO: trigger an event
// event to re-render metadata panel header when metadata is edited
this.parentView.parentView.trigger('li-metadata-widget-update', {});
this.parentView.render();

this.newDatum = false;
Expand All @@ -354,8 +355,7 @@ var MetadatumEditWidget = View.extend({
return false;
}
getMetadataRecord(this.item, this.fieldName)[tempKey] = tempValue;
// TODO: this.parentView.parentView.render();
return;
this.parentView.parentView.render();
}
this.item.addMetadata(tempKey, tempValue, saveCallback, errorCallback, {
field: this.fieldName,
Expand All @@ -377,7 +377,7 @@ var MetadatumEditWidget = View.extend({
}
delete getMetadataRecord(this.item, this.fieldName)[this.key];
getMetadataRecord(this.item, this.fieldName)[tempKey] = tempValue;
// TODO: this.parentView.parentView.render();
this.parentView.parentView.render();
return;
}
this.item.editMetadata(tempKey, this.key, tempValue, saveCallback, errorCallback, {
Expand Down Expand Up @@ -413,7 +413,7 @@ var JsonMetadatumEditWidget = MetadatumEditWidget.extend({

save: function (event) {
try {
MetadatumEditWidget.prototype.save.call(
return MetadatumEditWidget.prototype.save.call(
this, event, this.editor.get());
} catch (err) {
events.trigger('g:alert', {
Expand Down Expand Up @@ -449,9 +449,12 @@ var JsonMetadatumEditWidget = MetadatumEditWidget.extend({
});

wrap(MetadataWidget, 'initialize', function (initialize, settings) {
const result = initialize.call(this, settings);
try {
initialize.call(this, settings);
} catch (err) {
}
this.noSave = settings.noSave;
if (this.item.get('_modelType') === 'item') {
if (this.item && this.item.get('_modelType') === 'item') {
largeImageConfig.getConfigFile(this.item.get('folderId')).done((val) => {
this._limetadata = (val || {}).itemMetadata;
if (this._limetadata) {
Expand All @@ -461,11 +464,21 @@ wrap(MetadataWidget, 'initialize', function (initialize, settings) {
} else {
this._limetadata = null;
}
return result;
});

wrap(MetadataWidget, 'render', function (render) {
var metaDict = this.item.get(this.fieldName) || {};
let metaDict;
if (this.item.get(this.fieldName)) {
metaDict = this.item.get(this.fieldName) || {};
} else if (this.item[this.fieldName]) {
metaDict = this.item[this.fieldName] || {};
} else {
const fieldParts = this.fieldName.split('.');
metaDict = this.item.get(fieldParts[0]) || {};
fieldParts.slice(1).forEach((part) => {
metaDict = metaDict[part] || {};
});
}
var metaKeys = Object.keys(metaDict);
metaKeys.sort(localeSort);
if (this._limetadata) {
Expand All @@ -485,15 +498,16 @@ wrap(MetadataWidget, 'render', function (render) {
return origOrder.indexOf(a) - origOrder.indexOf(b);
});
}

// Metadata header
this.$el.html((this.MetadataWidgetTemplate || MetadataWidgetTemplate)({
this._sortedMetaKeys = metaKeys;
this._renderedMetaDict = metaDict;
const contents = (this.MetadataWidgetTemplate || MetadataWidgetTemplate)({
item: this.item,
title: this.title,
accessLevel: this.accessLevel,
AccessType: AccessType,
limetadata: this._limetadata
}));
});
this._renderHeader(contents);

// Append each metadatum
_.each(metaKeys, function (metaKey) {
Expand All @@ -506,6 +520,7 @@ wrap(MetadataWidget, 'render', function (render) {
fieldName: this.fieldName,
apiPath: this.apiPath,
limetadata: this._limetadata,
noSave: this.noSave,
onMetadataEdited: this.onMetadataEdited,
onMetadataAdded: this.onMetadataAdded
}).render().$el);
Expand All @@ -515,6 +530,17 @@ wrap(MetadataWidget, 'render', function (render) {
});

wrap(MetadataWidget, 'setItem', function (setItem, item) {
if (item !== this.item) {
this._limetadata = null;
if (item && item.get('_modelType') === 'item') {
largeImageConfig.getConfigFile(item.get('folderId')).done((val) => {
this._limetadata = (val || {}).itemMetadata;
if (this._limetadata) {
this.render();
}
});
}
}
setItem.call(this, item);
this.item.on('g:changed', function () {
this.render();
Expand Down Expand Up @@ -551,6 +577,43 @@ MetadataWidget.prototype.getModeFromValue = function (value, key) {
return _.isString(value) ? 'simple' : 'json';
};

MetadataWidget.prototype.addMetadata = function (evt, mode) {
var EditWidget = this.modes[mode].editor;
var value = (mode === 'json') ? '{}' : '';

var widget = new MetadatumWidget({
className: 'g-widget-metadata-row editing',
mode: mode,
key: '',
value: value,
item: this.item,
fieldName: this.fieldName,
noSave: this.noSave,
apiPath: this.apiPath,
accessLevel: this.accessLevel,
parentView: this,
onMetadataEdited: this.onMetadataEdited,
onMetadataAdded: this.onMetadataAdded
});
widget.$el.appendTo(this.$('.g-widget-metadata-container'));

new EditWidget({
item: this.item,
key: '',
value: value,
fieldName: this.fieldName,
noSave: this.noSave,
apiPath: this.apiPath,
accessLevel: this.accessLevel,
newDatum: true,
parentView: widget,
onMetadataEdited: this.onMetadataEdited,
onMetadataAdded: this.onMetadataAdded
})
.render()
.$el.appendTo(widget.$el);
};

MetadataWidget.prototype.addMetadataByKey = function (evt) {
const key = $(evt.target).attr('metadata-key');
// if this key already exists, just go to editing it
Expand Down Expand Up @@ -586,6 +649,7 @@ MetadataWidget.prototype.addMetadataByKey = function (evt) {
apiPath: this.apiPath,
accessLevel: this.accessLevel,
newDatum: true,
noSave: this.noSave,
parentView: widget,
limetadata: this._limetadata,
onMetadataEdited: this.onMetadataEdited,
Expand All @@ -595,9 +659,14 @@ MetadataWidget.prototype.addMetadataByKey = function (evt) {
.$el.appendTo(widget.$el);
};

export default {
MetadataWidget.prototype._renderHeader = function (contents) {
this.$el.html(contents);
};

export {
MetadataWidget,
MetadatumWidget,
MetadatumEditWidget,
JsonMetadatumEditWidget
JsonMetadatumEditWidget,
liMetadataKeyEntry
};

0 comments on commit e59f7a8

Please sign in to comment.