Skip to content

Commit

Permalink
Merge branch 'master' into update-with-master
Browse files Browse the repository at this point in the history
  • Loading branch information
naglepuff committed Oct 17, 2024
2 parents d53812c + 3f37985 commit 8ad50fd
Show file tree
Hide file tree
Showing 34 changed files with 813 additions and 271 deletions.
11 changes: 6 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
rev: v4.6.0
hooks:
- id: check-added-large-files
- id: check-ast
Expand All @@ -25,6 +25,7 @@ repos:
- id: fix-byte-order-marker
- id: forbid-new-submodules
- id: mixed-line-ending
- id: no-commit-to-branch
- id: trailing-whitespace
- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.10.0
Expand All @@ -42,7 +43,7 @@ repos:
files: README.rst
name: rst-linter of README.rst
- repo: https://github.com/codespell-project/codespell
rev: v2.2.6
rev: v2.3.0
hooks:
- id: codespell
args:
Expand All @@ -61,13 +62,13 @@ repos:
- './histomicsui/web_client/package.json'
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.1.3
rev: v0.6.0
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
types_or: [python, pyi, jupyter]
- repo: https://github.com/asottile/pyupgrade
rev: v3.15.0
rev: v3.17.0
hooks:
- id: pyupgrade
args:
Expand All @@ -78,6 +79,6 @@ repos:
hooks:
- id: autopep8
- repo: https://github.com/PyCQA/flake8
rev: 6.1.0
rev: 7.1.1
hooks:
- id: flake8
30 changes: 17 additions & 13 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
=======================================
HistomicsUI |build-status| |codecov-io|
=======================================
===========
HistomicsUI
===========

|build-status| |codecov-io| |doi-badge|

.. |build-status| image:: https://circleci.com/gh/DigitalSlideArchive/HistomicsUI.svg?style=svg
:target: https://circleci.com/gh/DigitalSlideArchive/HistomicsUI
:alt: Build Status

.. |codecov-io| image:: https://img.shields.io/codecov/c/github/DigitalSlideArchive/HistomicsUI.svg
:target: https://codecov.io/github/DigitalSlideArchive/HistomicsUI?branch=master
:alt: codecov.io

.. |doi-badge| image:: https://img.shields.io/badge/DOI-10.5281%2Fzenodo.5474914-blue.svg
:target: https://zenodo.org/doi/10.5281/zenodo.5474914

Organize, visualize, and analyze histology images.

Expand Down Expand Up @@ -87,7 +100,7 @@ If you are making changes to the HistomicsUI frontend, you can make Girder watch
Annotations and Metadata from Jobs
----------------------------------

This handles ingesting annotations and metadata that are uploaded and associating them with existing large image items in the Girder database. These annotations and metadata re commonly generated through jobs, such as HistomicTK tasks, but can also be added manually.
This handles ingesting annotations and metadata that are uploaded and associating them with existing large image items in the Girder database. These annotations and metadata are commonly generated through jobs, such as HistomicTK tasks, but can also be added manually.

If a file is uploaded to the Girder system that includes a ``reference`` record, and that ``reference`` record contains an ``identifier`` field and at least one of a ``fileId`` and an ``itemId`` field, specific identifiers can be used to ingest the results. If a ``userId`` is specified in the ``reference`` record, permissions for adding the annotation or metadata are associated with that user.

Expand Down Expand Up @@ -117,15 +130,6 @@ This work was funded in part by the NIH grant U24-CA194362-01_.
.. _Girder Worker: https://girder-worker.readthedocs.io/en/latest/
.. _large_image: https://github.com/girder/large_image
.. _slicer_cli_web: https://github.com/girder/slicer_cli_web
.. _slicer execution model: https://www.slicer.org/slicerWiki/index.php/Slicer3:Execution_Model_Documentation
.. _celery: http://www.celeryproject.org/
.. _HistomicsTK: https://github.com/DigitalSlideArchive/HistomicsTK
.. _Digital Slide Archive: https://github.com/DigitalSlideArchive/digital_slide_archive

.. |build-status| image:: https://circleci.com/gh/DigitalSlideArchive/HistomicsUI.svg?style=svg
:target: https://circleci.com/gh/DigitalSlideArchive/HistomicsUI
:alt: Build Status

.. |codecov-io| image:: https://img.shields.io/codecov/c/github/DigitalSlideArchive/HistomicsUI.svg
:target: https://codecov.io/github/DigitalSlideArchive/HistomicsUI?branch=master
:alt: codecov.io
3 changes: 2 additions & 1 deletion histomicsui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ def validateLoginSessionExpiryMinutes(doc):
PluginSettings.HUI_HELP_URL,
PluginSettings.HUI_HELP_TOOLTIP,
PluginSettings.HUI_HELP_TEXT,
PluginSettings.HUI_LOGIN_TEXT,
})
def validateHistomicsUIHelp(doc):
pass
Expand Down Expand Up @@ -396,7 +397,7 @@ def lookUpToken(token, parentType, parent):
# Auto-ingest annotations into database when a file with an identifier
# ending in 'AnnotationFile' is uploaded (usually .anot files).
events.bind('data.process', 'histomicsui.annotations', handlers.process_annotations)
# Auto-ingest metadta into parent when a file with an identifier
# Auto-ingest metadata into parent when a file with an identifier
# ending in 'ItemMetadata' is uploaded (usually .meta files).
events.bind('data.process', 'histomicsui.metadata', handlers.process_metadata)

Expand Down
1 change: 1 addition & 0 deletions histomicsui/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ class PluginSettings:
HUI_HELP_URL = 'histomicsui.help_url'
HUI_HELP_TOOLTIP = 'histomicsui.help_tooltip'
HUI_HELP_TEXT = 'histomicsui.help_text'
HUI_LOGIN_TEXT = 'histomicsui.login_text'
HUI_LOGIN_SESSION_EXPIRY_MINUTES = 'histomicsui.login_session_expiry_minutes'
5 changes: 4 additions & 1 deletion histomicsui/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import cherrypy
import girder.utility
import girder_large_image_annotation
import orjson
from girder.constants import AccessType
from girder.exceptions import RestException
Expand Down Expand Up @@ -97,7 +98,9 @@ def read_entire_file():
if time.time() - startTime > 10:
logger.info('Decoded json in %5.3fs', time.time() - startTime)

if not isinstance(data, list):
if not isinstance(data, list) or (
hasattr(girder_large_image_annotation.utils, 'isGeoJSON') and
girder_large_image_annotation.utilsisGeoJSON(data)):
data = [data]

for annotation in data:
Expand Down
1 change: 1 addition & 0 deletions histomicsui/rest/hui_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def getPublicSettings(self, params):
keys = [
PluginSettings.HUI_BRAND_NAME,
PluginSettings.HUI_DEFAULT_DRAW_STYLES,
PluginSettings.HUI_LOGIN_TEXT,
PluginSettings.HUI_PANEL_LAYOUT,
PluginSettings.HUI_QUARANTINE_FOLDER,
PluginSettings.HUI_WEBROOT_PATH,
Expand Down
28 changes: 27 additions & 1 deletion histomicsui/rest/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import datetime
import os

from girder import logger
from girder.api import access
from girder.api.describe import Description, autoDescribeRoute, describeRoute
from girder.api.rest import RestException, boundHandler, filtermodel
Expand Down Expand Up @@ -44,6 +45,8 @@ def addSystemEndpoints(apiRoot):
apiRoot.item.route('GET', ('query',), getItemsByQuery)
# Added to the folder route
apiRoot.folder.route('GET', ('query',), getFoldersByQuery)
# Added to the file route
apiRoot.file.route('GET', ('query',), getFilesByQuery)
# Added to the system route
apiRoot.system.route('PUT', ('restart',), restartServer)
apiRoot.system.route('GET', ('setting', 'default'), getSettingDefault)
Expand Down Expand Up @@ -197,6 +200,22 @@ def getItemsByQuery(self, query, limit, offset, sort):
return Item().findWithPermissions(query, offset=offset, limit=limit, sort=sort, user=user)


@access.admin(scope=TokenScope.DATA_READ)
@filtermodel(model=File)
@autoDescribeRoute(
Description('List files that match a query.')
.responseClass('File', array=True)
.jsonParam('query', 'Find files that match this Mongo query.',
required=True, requireObject=True)
.pagingParams(defaultSort='_id')
.errorResponse(),
)
@boundHandler()
def getFilesByQuery(self, query, limit, offset, sort):
user = self.getCurrentUser()
return File().findWithPermissions(query, offset=offset, limit=limit, sort=sort, user=user)


@access.public(scope=TokenScope.DATA_READ)
@filtermodel(model=Folder)
@autoDescribeRoute(
Expand Down Expand Up @@ -461,5 +480,12 @@ def getMultipleResourcePaths(self, resources):
model = ModelImporter.model(kind)
for id in resources[kind]:
doc = model.load(id=id, user=user, level=AccessType.READ)
results[kind][id] = path_util.getResourcePath(kind, doc, user=user)
if doc is None:
logger.info(f'Failed to load {kind} {id}')
continue
try:
results[kind][id] = path_util.getResourcePath(kind, doc, user=user)
except Exception:
logger.info(f'Failed to resolve path for {kind} {id} {doc}')
continue
return results
9 changes: 9 additions & 0 deletions histomicsui/web_client/dialogs/metadataPlot.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import metadataPlotDialog from '../templates/dialogs/metadataPlot.pug';
import '../stylesheets/dialogs/metadataPlot.styl';

const View = girder.views.View;
const $ = girder.$;
const girderModal = girder.utilities.girderModal;

const MetadataPlotDialog = View.extend({
events: {
Expand All @@ -19,6 +22,7 @@ const MetadataPlotDialog = View.extend({
plotOptions: this.plotOptions
})
).girderModal(this);

return this;
},

Expand All @@ -34,6 +38,11 @@ const MetadataPlotDialog = View.extend({
configOptions[series] = val;
}
});
['u'].forEach((series) => {
const opts = this.$('#h-plot-series-' + series + ' option');
const val = opts.filter((idx, o) => o.selected).map((idx, o) => $(o).val()).get();
configOptions[series] = val.length ? val : undefined;
});
this.result = configOptions;
this.$el.modal('hide');
}
Expand Down
2 changes: 2 additions & 0 deletions histomicsui/web_client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@
"bootstrap-submenu": "^2.0.4",
"copy-webpack-plugin": "^4.5.2",
"petite-vue": "^0.4.1",
"plotly.js": "2.34.0",
"sinon": "^2.1.0",
"tinycolor2": "~1.4.1",
"url-search-params-polyfill": "^8.1.1",
"vue": "^2.7.16",
"uuid": "^8.3.2",
"vue-color": "^2.8.1",
"vue-loader": "~15.9.8",
"vue-template-compiler": "~2.6.14"
Expand Down
1 change: 1 addition & 0 deletions histomicsui/web_client/panels/AnnotationSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ var AnnotationSelector = Panel.extend({
this.trigger('h:deleteAnnotation', models[id]);
}
});
this.collection.trigger('h:refreshed', this.collection);
return null;
});
},
Expand Down
33 changes: 23 additions & 10 deletions histomicsui/web_client/panels/DrawWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@ var DrawWidget = Panel.extend({
this.$('button.h-draw[data-type="' + this._drawingType + '"]').addClass('active');
this.drawElement(undefined, this._drawingType);
}
this._bindHUIModeChange();
this._updateConstraintValueInputs();
return this;
},

_bindHUIModeChange() {
if (this.viewer.annotationLayer && this.viewer.annotationLayer._boundHUIModeChange !== this) {
this.viewer.annotationLayer._boundHUIModeChange = this;
this.viewer.annotationLayer.geoOff(geo.event.annotation.mode);
Expand All @@ -154,8 +160,6 @@ var DrawWidget = Panel.extend({
}
});
}
this._updateConstraintValueInputs();
return this;
},

/**
Expand Down Expand Up @@ -625,6 +629,9 @@ var DrawWidget = Panel.extend({
* if it hasn't changed.
*/
drawElement(evt, type, forceRefresh) {
if (type) {
this._bindHUIModeChange();
}
var $el;
if (evt) {
$el = this.$(evt.currentTarget);
Expand Down Expand Up @@ -661,7 +668,12 @@ var DrawWidget = Panel.extend({
}

this.viewer.startDrawMode(type, options)
.then((element, annotations, opts) => this._addDrawnElements(element, annotations, opts));
.then((element, annotations, opts) => this._addDrawnElements(element, annotations, opts))
.fail(() => {
if (this._drawingType && this._drawingType !== this.viewer.annotationLayer.mode() && this.viewer.annotationLayer.mode() !== 'edit') {
this.drawElement(undefined, this._drawingType, !!this._drawingType);
}
});
}
this.$('button.h-draw[data-type]').removeClass('active');
if (this._drawingType) {
Expand All @@ -676,6 +688,7 @@ var DrawWidget = Panel.extend({
this.drawElement(undefined, null);
this.viewer.annotationLayer._boundHUIModeChange = false;
this.viewer.annotationLayer.geoOff(geo.event.annotation.state);
this.viewer.annotationLayer.geoOff(geo.event.annotation.mode);
},

drawingType() {
Expand Down Expand Up @@ -832,6 +845,12 @@ var DrawWidget = Panel.extend({
* @param {object} group The new group.
*/
_setStyleGroup(group) {
group = Object.assign({}, group);
Object.keys(group).forEach((k) => {
if (!['fillColor', 'lineColor', 'lineWidth', 'label', 'group', 'id'].includes(k)) {
delete group[k];
}
});
this._style.set(group);
if (!group.group && this._style.id !== this.parentView._defaultGroup) {
this._style.set('group', this._style.id);
Expand Down Expand Up @@ -923,13 +942,7 @@ var DrawWidget = Panel.extend({

this._updateConstraintValueInputs();

if (opts.size_mode === 'fixed_aspect_ratio') {
this.viewer.startDrawMode(this._drawingType, {modeOptions: {constraint: opts.fixed_width / opts.fixed_height}});
} else if (opts.size_mode === 'fixed_size') {
this.viewer.startDrawMode(this._drawingType, {modeOptions: {constraint: {width: opts.fixed_width, height: opts.fixed_height}}});
} else {
this.viewer.startDrawMode(this._drawingType);
}
this.drawElement(null, this._drawingType, true);
},

/**
Expand Down
Loading

0 comments on commit 8ad50fd

Please sign in to comment.