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 nuclei inferral UI #5669

Merged
merged 17 commits into from
Aug 24, 2021
Merged
Show file tree
Hide file tree
Changes from 3 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
2 changes: 1 addition & 1 deletion conf/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ features {
autoBrushReadyDatasets = []
taskReopenAllowedInSeconds = 30
allowDeleteDatasets = true
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
10 changes: 10 additions & 0 deletions frontend/javascripts/admin/admin_rest_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,16 @@ export function startComputeMeshFileJob(
);
}

export function startNucleiInferralJob(
organizationName: string,
datasetName: string,
layerName: string,
): Promise<APIJob> {
return Request.receiveJSON(
`/api/jobs/run/inferNuclei/${organizationName}/${datasetName}?layerName=${layerName}`,
);
}

export function getDatasetDatasource(
dataset: APIMaybeUnimportedDataset,
): Promise<APIDataSourceWithMessages> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
* @flow
*/
import type { Dispatch } from "redux";
import { Tooltip, Button } from "antd";
import { EditOutlined, InfoCircleOutlined } from "@ant-design/icons";
import { Tooltip, Button, Dropdown, Menu } from "antd";
import { EditOutlined, InfoCircleOutlined, StarOutlined } from "@ant-design/icons";
import { connect } from "react-redux";
import Markdown from "react-remarkable";
import React from "react";
Expand All @@ -26,7 +26,9 @@ import {
import ButtonComponent from "oxalis/view/components/button_component";
import EditableTextLabel from "oxalis/view/components/editable_text_label";
import Model from "oxalis/model";
import features from "features";
import Store, { type OxalisState, type Task, type Tracing } from "oxalis/store";
import NucleiInferralModal from "oxalis/view/right-border-tabs/nuclei_inferral_modal";

type StateProps = {|
tracing: Tracing,
Expand All @@ -42,6 +44,10 @@ type DispatchProps = {|

type Props = {| ...StateProps, ...DispatchProps |};

type State = {
showNucleiInferralModal: boolean,
};

const shortcuts = [
{
key: "1",
Expand Down Expand Up @@ -122,7 +128,11 @@ export function convertPixelsToNm(
return lengthInPixel * zoomValue * getBaseVoxel(dataset.dataSource.scale);
}

class DatasetInfoTabView extends React.PureComponent<Props> {
class DatasetInfoTabView extends React.PureComponent<Props, State> {
state = {
showNucleiInferralModal: false,
};

setAnnotationName = (newName: string) => {
this.props.setAnnotationName(newName);
};
Expand Down Expand Up @@ -185,6 +195,40 @@ class DatasetInfoTabView extends React.PureComponent<Props> {
) : null;
}

getPremiumFeaturesMenu = () => {
// TODO: Integrate the new jobsEnabled field from the dataset.
if (!features().jobsEnabled) {
return (
<Tooltip title="Premium features are only available for datasets hosted natively and not on other datastores.">
<StarOutlined className="info-tab-icon" width={24} height={24} /> Premium Features
</Tooltip>
);
}
const overlay = (
<Menu>
<Menu.Item onClick={() => this.setState({ showNucleiInferralModal: true })}>
<Tooltip title="Start a job that automatically detects nuclei for this dataset.">
Start Nuclei Inferral
</Tooltip>
</Menu.Item>
</Menu>
);
MichaelBuessemeyer marked this conversation as resolved.
Show resolved Hide resolved
return (
<tr>
<td style={{ paddingRight: 4, paddingTop: 10, verticalAlign: "top" }}>
<StarOutlined className="info-tab-icon" style={{ fontSize: 18 }} />
MichaelBuessemeyer marked this conversation as resolved.
Show resolved Hide resolved
</td>
<Dropdown overlay={overlay}>
<td>
<Button type="link" style={{ padding: 0 }}>
Process Dataset
</Button>
</td>
</Dropdown>
</tr>
);
};

getDatasetName(isDatasetViewMode: boolean) {
const {
name: datasetName,
Expand Down Expand Up @@ -370,7 +414,7 @@ class DatasetInfoTabView extends React.PureComponent<Props> {
}

render() {
const { dataset, activeResolution } = this.props;
const { dataset, activeResolution, activeUser } = this.props;
const isDatasetViewMode =
Store.getState().temporaryConfiguration.controlMode === ControlModeEnum.VIEW;

Expand Down Expand Up @@ -450,8 +494,18 @@ class DatasetInfoTabView extends React.PureComponent<Props> {
</tr>
</Tooltip>
{resolutionInfo}

{activeUser != null && (activeUser.isDatasetManager || activeUser.isAdmin)
? this.getPremiumFeaturesMenu()
: null}
</tbody>
</table>
{this.state.showNucleiInferralModal ? (
<NucleiInferralModal
dataset={dataset}
handleClose={() => this.setState({ showNucleiInferralModal: false })}
/>
) : null}
</div>

<div className="info-tab-block">{this.getTracingStatistics()}</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// @flow
import React, { useEffect, useState } from "react";
import { type APIDataset } from "types/api_flow_types";
import { Modal, Select, Button } from "antd";
import { startNucleiInferralJob } from "admin/admin_rest_api";
import { getColorLayers } from "oxalis/model/accessors/dataset_accessor";
import Toast from "libs/toast";

type Props = {
dataset: APIDataset,
handleClose: () => void,
};

export default function NucleiInferralModal(props: Props) {
const { dataset, handleClose } = props;
const [selectedColorLayerName, setSelectedColorLayerName] = useState(null);
const colorLayers = getColorLayers(dataset);
MichaelBuessemeyer marked this conversation as resolved.
Show resolved Hide resolved
useEffect(() => {
if (colorLayers.length === 1) {
setSelectedColorLayerName(colorLayers[0].name);
}
});
if (colorLayers.length < 1) {
return null;
}

const onChange = selectedLayerName => {
setSelectedColorLayerName(selectedLayerName);
};

const startJob = async () => {
if (selectedColorLayerName == null) {
return;
}
try {
await startNucleiInferralJob(
dataset.owningOrganization,
dataset.name,
selectedColorLayerName,
);
Toast.info(
"Starting the nuclei inferral job succeeded. You can look in the Processing Jobs view under Administration for details on the progress of this job.",
);
handleClose();
} catch (error) {
console.error(error);
Toast.error(
"Starting the nuclei inferral job failed. Please look in the console for more details.",
);
handleClose();
}
};

return (
<Modal title="Start Nuclei Inferal" onCancel={handleClose} visible width={700} footer={null}>
<p>
Start a job that automatically detects nuclei for this dataset. This job creates a copy of
this dataset once it has finished. This copy contains the detected nuclei as a segmentation
layer.
</p>
<br />
<div style={{ textAlign: "center" }}>
<img
src="/assets/images/nuclei_inferral_example.jpg"
alt="Nuclei inferral example"
style={{ width: 400, height: "auto", borderRadius: 3 }}
/>
</div>
<br />
{colorLayers.length > 1 ? (
<React.Fragment>
<p>
The detection approach uses a single color layer to predict the nuclei. Please select
the layer that should be used for detection.
</p>
<div style={{ textAlign: "center" }}>
<Select
showSearch
style={{ width: 200 }}
placeholder="Select a color layer"
optionFilterProp="children"
onChange={onChange}
filterOption={(input, option) =>
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
>
{colorLayers.map(colorLayer => (
<Select.Option key={colorLayer.name} value={colorLayer.name}>
{colorLayer.name}
</Select.Option>
))}
</Select>
</div>
<br />
</React.Fragment>
) : null}
<div style={{ textAlign: "center" }}>
<Button type="primary" disabled={selectedColorLayerName == null} onClick={startJob}>
Start Nuclei Inferral
</Button>
</div>
</Modal>
);
}
Binary file added public/images/nuclei_inferral_example.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.