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

Add UI to globalize incomplete floodfill(s) #5905

Merged
merged 21 commits into from
Jan 26, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
57a6f33
some tag related tweaks
philippotto Dec 13, 2021
ec30401
add button for globalizing floodfill
philippotto Dec 13, 2021
01041ff
fix newDataSetName
philippotto Dec 13, 2021
a4697f6
Pass user auth token to globalizeFloodfills job
fm3 Dec 14, 2021
f69116a
Merge branch 'globalize-floodfill' of github.com:scalableminds/webkno…
fm3 Dec 14, 2021
575d1a3
Merge branch 'master' into globalize-floodfill
fm3 Dec 14, 2021
c5aa03d
fix orga access after master merge
fm3 Dec 14, 2021
9c39a5e
Merge branch 'master' of github.com:scalableminds/webknossos into glo…
philippotto Jan 11, 2022
abb06c5
let user choose name for new dataset with globalized floodfills
philippotto Jan 11, 2022
971072e
Merge branch 'master' of github.com:scalableminds/webknossos into glo…
philippotto Jan 21, 2022
3b577a5
don't hardcode segmentation layer name
philippotto Jan 21, 2022
03ad4d7
right-align globalize button
philippotto Jan 21, 2022
ef302ce
Merge branch 'master' of github.com:scalableminds/webknossos into glo…
philippotto Jan 26, 2022
b7f6571
Update frontend/javascripts/oxalis/view/right-border-tabs/bounding_bo…
philippotto Jan 26, 2022
7f5cb8e
Update frontend/javascripts/oxalis/view/right-border-tabs/bounding_bo…
philippotto Jan 26, 2022
62726d8
incorporate feedback
philippotto Jan 26, 2022
7e99efb
Merge branch 'globalize-floodfill' of github.com:scalableminds/webkno…
philippotto Jan 26, 2022
1204ddd
disable jobs again
philippotto Jan 26, 2022
30dba2e
update changelog
philippotto Jan 26, 2022
402e9f1
format
philippotto Jan 26, 2022
694f70a
Merge branch 'master' into globalize-floodfill
philippotto Jan 26, 2022
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
33 changes: 33 additions & 0 deletions app/controllers/JobsController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,39 @@ class JobsController @Inject()(jobDAO: JobDAO,
}
}

def runGlobalizeFloodfills(
organizationName: String,
dataSetName: String,
newDataSetName: Option[String],
layerName: Option[String],
annotationId: Option[String],
annotationType: Option[String],
): Action[AnyContent] =
sil.SecuredAction.async { implicit request =>
log(Some(slackNotificationService.noticeFailedJobRequest)) {
for {
organization <- organizationDAO.findOneByName(organizationName) ?~> Messages("organization.notFound",
organizationName)
_ <- bool2Fox(request.identity._organization == organization._id) ?~> "job.globalizeFloodfill.notAllowed.organization" ~> FORBIDDEN
dataSet <- dataSetDAO.findOneByNameAndOrganization(dataSetName, organization._id) ?~> Messages(
"dataSet.notFound",
dataSetName) ~> NOT_FOUND
command = "globalize_floodfills"
commandArgs = Json.obj(
"organization_name" -> organizationName,
"dataset_name" -> dataSetName,
"new_dataset_name" -> newDataSetName,
"layer_name" -> layerName,
"annotation_id" -> annotationId,
"annotation_type" -> annotationType,
"webknossos_token" -> RpcTokenHolder.webKnossosToken,
)
job <- jobService.submitJob(command, commandArgs, request.identity, dataSet._dataStore) ?~> "job.couldNotRunGlobalizeFloodfills"
js <- jobService.publicWrites(job)
} yield Ok(js)
}
}

def runExportTiffJob(organizationName: String,
dataSetName: String,
bbox: String,
Expand Down
2 changes: 1 addition & 1 deletion conf/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ features {
taskReopenAllowedInSeconds = 30
allowDeleteDatasets = true
# to enable jobs for local development, use "yarn enable-jobs" to also activate it in the database
jobsEnabled = false
jobsEnabled = true
philippotto marked this conversation as resolved.
Show resolved Hide resolved
# For new users, the dashboard will show a banner which encourages the user to check out the following dataset.
# If isDemoInstance == true, `/createExplorative/hybrid/true` is appended to the URL so that a new tracing is opened.
# If isDemoInstance == false, `/view` is appended to the URL so that it's opened in view mode (since the user might not
Expand Down
2 changes: 2 additions & 0 deletions conf/messages
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ job.couldNotRunCubing = Failed to start WKW conversion job.
job.couldNotRunTiffExport = Failed to start Tiff export job.
job.couldNotRunComputeMeshFile = Failed to start mesh file computation job.
job.couldNotRunNucleiInferral = Failed to start nuclei inferral job.
job.couldNotRunGlobalizeFloodfills = Failed to start job for globalizing floodfills.
job.disabled = Long-running jobs are not enabled for this webKnossos instance.
jobs.worker.notFound = Could not find this worker in the database.
job.export.fileNotFound = Exported file not found. The link may be expired.
Expand All @@ -343,6 +344,7 @@ job.export.tiff.edgeLengthExceeded = An edge length of the selected bounding box
job.export.notAllowed.organization = Currently tiff export is only allowed for datasets of your own organization.
job.inferNuclei.notAllowed.organization = Currently nuclei inferral is only allowed for datasets of your own organization.
job.meshFile.notAllowed.organization = Calculating mesh files is only allowed for datasets of your own organization.
job.globalizeFloodfill.notAllowed.organization = Globalizing floodfills is only allowed for datasets of your own organization.

agglomerateSkeleton.failed=Could not generate agglomerate skeleton.

Expand Down
1 change: 1 addition & 0 deletions conf/webknossos.latest.routes
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ GET /jobs/run/convertToWkw/:organizationName/:dataSetName c
GET /jobs/run/computeMeshFile/:organizationName/:dataSetName controllers.JobsController.runComputeMeshFileJob(organizationName: String, dataSetName: String, layerName: String, mag: String, agglomerateView: Option[String])
GET /jobs/run/exportTiff/:organizationName/:dataSetName controllers.JobsController.runExportTiffJob(organizationName: String, dataSetName: String, bbox: String, layerName: Option[String], tracingId: Option[String], tracingVersion: Option[String], annotationId: Option[String], annotationType: Option[String], hideUnmappedIds: Option[Boolean], mappingName: Option[String], mappingType: Option[String])
GET /jobs/run/inferNuclei/:organizationName/:dataSetName controllers.JobsController.runInferNucleiJob(organizationName: String, dataSetName: String, layerName: Option[String])
GET /jobs/run/globalizeFloodfills/:organizationName/:dataSetName controllers.JobsController.runGlobalizeFloodfills(organizationName: String, dataSetName: String, newDataSetName: Option[String], layerName: Option[String], annotationId: Option[String], annotationType: Option[String])
GET /jobs/:id controllers.JobsController.get(id: String)
GET /jobs/:id/downloadExport/:exportFileName controllers.JobsController.downloadExport(id: String, exportFileName: String)
POST /jobs/:id/status controllers.WKRemoteWorkerController.updateJobStatus(id: String)
13 changes: 13 additions & 0 deletions frontend/javascripts/admin/admin_rest_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -1011,6 +1011,19 @@ export function startNucleiInferralJob(
);
}

export function startGlobalizeFloodfillsJob(
organizationName: string,
datasetName: string,
newDataSetName: string,
layerName: string,
annotationId: string,
annotationType: APIAnnotationType,
): Promise<APIJob> {
return Request.receiveJSON(
`/api/jobs/run/globalizeFloodfills/${organizationName}/${datasetName}?newDataSetName=${newDataSetName}&layerName=${layerName}&annotationId=${annotationId}&annotationType=${annotationType}`,
);
}

export function getDatasetDatasource(
dataset: APIMaybeUnimportedDataset,
): Promise<APIDataSourceWithMessages> {
Expand Down
29 changes: 17 additions & 12 deletions frontend/javascripts/dashboard/advanced_dataset/dataset_table.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
// @flow

import { Table, Tag } from "antd";
import {
CheckCircleOutlined,
CloseCircleOutlined,
PlusOutlined,
WarningOutlined,
} from "@ant-design/icons";
import { Link } from "react-router-dom";
import { Table, Tag, Tooltip } from "antd";
import * as React from "react";
import { CheckCircleOutlined, CloseCircleOutlined, PlusOutlined } from "@ant-design/icons";
import _ from "lodash";
import { Link } from "react-router-dom";
import dice from "dice-coefficient";
import update from "immutability-helper";

import EditableTextIcon from "oxalis/view/components/editable_text_icon";
import type {
APITeam,
APIMaybeUnimportedDataset,
APIDatasetId,
APIDataset,
} from "types/api_flow_types";
import { stringToColor, formatScale } from "libs/format_utils";
import type { DatasetFilteringMode } from "dashboard/dataset_view";
import DatasetAccessListView from "dashboard/advanced_dataset/dataset_access_list_view";
import DatasetActionView from "dashboard/advanced_dataset/dataset_action_view";
import FormattedDate from "components/formatted_date";
import { getDatasetExtentAsString } from "oxalis/model/accessors/dataset_accessor";
import { stringToColor, formatScale } from "libs/format_utils";
import { trackAction } from "oxalis/model/helpers/analytics";
import CategorizationLabel from "oxalis/view/components/categorization_label";
import DatasetAccessListView from "dashboard/advanced_dataset/dataset_access_list_view";
import DatasetActionView from "dashboard/advanced_dataset/dataset_action_view";
import EditableTextIcon from "oxalis/view/components/editable_text_icon";
import FixedExpandableTable from "components/fixed_expandable_table";
import FormattedDate from "components/formatted_date";
import * as Utils from "libs/utils";
import CategorizationLabel from "oxalis/view/components/categorization_label";

const { Column } = Table;

Expand Down Expand Up @@ -255,9 +260,9 @@ class DatasetTable extends React.PureComponent<Props, State> {
/>
</div>
) : (
<div style={{ color: "@disabled-color" }}>
Not tags available for inactive datasets.
</div>
<Tooltip title="No tags available for inactive datasets">
<WarningOutlined style={{ color: "@disabled-color" }} />
</Tooltip>
)
}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ type FilterProps = {|
export default function CategorizationLabel({ tag, kind, onClick, onClose, closable }: LabelProps) {
return (
<Tooltip title={`Click to only show ${kind} with this tag.`}>
<Tag color={stringToColor(tag)} onClick={onClick} onClose={onClose} closable={closable}>
<Tag
color={stringToColor(tag)}
onClick={onClick}
onClose={onClose}
closable={closable}
style={{ cursor: "pointer" }}
>
{tag}
</Tag>
</Tooltip>
Expand Down Expand Up @@ -63,6 +69,7 @@ export function CategorizationSearch({ searchTags, setTags, localStorageSavingKe
onClose={() => {
removeTag(tag);
}}
style={{ cursor: "pointer" }}
philippotto marked this conversation as resolved.
Show resolved Hide resolved
closable
>
{tag}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
/**
* tracing_settings_view.js
* @flow
*/
import { Tooltip } from "antd";
// @flow
import { Button, Tooltip } from "antd";
import { PlusSquareOutlined } from "@ant-design/icons";
import { useSelector, useDispatch } from "react-redux";
import React, { useState } from "react";
import _ from "lodash";

import { setPositionAction } from "oxalis/model/actions/flycam_actions";
import type { Vector3, Vector6, BoundingBoxType } from "oxalis/constants";
import { UserBoundingBoxInput } from "oxalis/view/components/setting_input_views";
import { getSomeTracing } from "oxalis/model/accessors/tracing_accessor";
import type { Vector3, Vector6, BoundingBoxType } from "oxalis/constants";
import {
changeUserBoundingBoxAction,
addUserBoundingBoxAction,
deleteUserBoundingBoxAction,
} from "oxalis/model/actions/annotation_actions";
import { getSomeTracing } from "oxalis/model/accessors/tracing_accessor";
import { hasVolumeTracings } from "oxalis/model/accessors/volumetracing_accessor";
import { setPositionAction } from "oxalis/model/actions/flycam_actions";
import { startGlobalizeFloodfillsJob } from "admin/admin_rest_api";
import ExportBoundingBoxModal from "oxalis/view/right-border-tabs/export_bounding_box_modal";
import * as Utils from "libs/utils";
import features from "features";

import ExportBoundingBoxModal from "oxalis/view/right-border-tabs/export_bounding_box_modal";
const GLOBALIZE_FLOODFILL_REGEX = /Limits of flood-fill \(source_id=(\d+), target_id=(\d+), seed=([\d,]+), timestamp=(\d+)\)/;
philippotto marked this conversation as resolved.
Show resolved Hide resolved

export default function BoundingBoxTab() {
const [selectedBoundingBoxForExport, setSelectedBoundingBoxForExport] = useState(null);
const tracing = useSelector(state => state.tracing);
const dataset = useSelector(state => state.dataset);
const activeUser = useSelector(state => state.activeUser);
const { userBoundingBoxes } = getSomeTracing(tracing);

const dispatch = useDispatch();
Expand Down Expand Up @@ -60,8 +62,38 @@ export default function BoundingBoxTab() {
setPosition(center);
}

const showGlobalizeFloodfillsButton =
features().jobsEnabled &&
activeUser != null &&
hasVolumeTracings(tracing) != null &&
userBoundingBoxes.some(bbox => bbox.name.match(GLOBALIZE_FLOODFILL_REGEX) != null);

const onGlobalizeFloodfills = () => {
startGlobalizeFloodfillsJob(
dataset.owningOrganization,
dataset.name,
`${dataset.name}_with_floodfills`,
"segmentation",
tracing.annotationId,
tracing.annotationType,
);
};

return (
<div className="padded-tab-content" style={{ minWidth: 300 }}>
{showGlobalizeFloodfillsButton ? (
<Tooltip title="For this annotation some floodfill operations were aborted prematurely, because they covered a too big volume. webKnossos can finish these operations via a long-running job.">
<Button size="small" style={{ marginBottom: 4 }} onClick={onGlobalizeFloodfills}>
<i
className="fas fa-fill-drip"
style={{
opacity: false ? 0.5 : 1,
}}
/>
Globalize Flood-Fills
</Button>
</Tooltip>
) : null}
{userBoundingBoxes.length > 0 ? (
userBoundingBoxes.map(bb => (
<UserBoundingBoxInput
Expand Down