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 download modal to dataset view actions #6283

Merged
merged 13 commits into from
Jun 21, 2022
Merged
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
### Added
- Added a image data download speed indicator to the statusbar. On hover a tooltip is shown that show the total amount of downloaded shard data. [#6269](https://github.com/scalableminds/webknossos/pull/6269)
- Added a warning for when the resolution in the XY viewport on z=1-downsampled datasets becomes too low, explaining the problem and how to mitigate it. [#6255](https://github.com/scalableminds/webknossos/pull/6255)
- Provide a UI to download/export a dataset in view-mode. The UI explains how to access the data with the python library. [#6283](https://github.com/scalableminds/webknossos/pull/6283)
- Added the possibility to view and download older versions of read-only annotations. [#6274](https://github.com/scalableminds/webknossos/pull/6274)

### Changed
Expand Down
1 change: 1 addition & 0 deletions frontend/javascripts/oxalis/default_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ const defaultState: OxalisState = {
showDropzoneModal: false,
showVersionRestore: false,
showDownloadModal: false,
showPythonClientModal: false,
showShareModal: false,
storedLayouts: {},
isImportingMesh: false,
Expand Down
15 changes: 11 additions & 4 deletions frontend/javascripts/oxalis/model/actions/ui_actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,14 @@ type SetThemeAction = {
type: "SET_THEME";
value: Theme;
};

type SetDownloadModalVisibilityAction = {
type: "SET_DOWNLOAD_MODAL_VISIBILITY";
visible: boolean;
};

type SetPythonClientModalVisibilityAction = {
type: "SET_PYTHON_MODAL_VISIBILITY";
visible: boolean;
};
type SetShareModalVisibilityAction = {
type: "SET_SHARE_MODAL_VISIBILITY";
visible: boolean;
Expand All @@ -68,6 +70,7 @@ export type UiAction =
| CycleToolAction
| SetThemeAction
| SetDownloadModalVisibilityAction
| SetPythonClientModalVisibilityAction
| SetShareModalVisibilityAction
| SetBusyBlockingInfoAction;
export const setDropzoneModalVisibilityAction = (
Expand Down Expand Up @@ -117,14 +120,18 @@ export const setThemeAction = (value: Theme): SetThemeAction => ({
type: "SET_THEME",
value,
});

export const setDownloadModalVisibilityAction = (
visible: boolean,
): SetDownloadModalVisibilityAction => ({
type: "SET_DOWNLOAD_MODAL_VISIBILITY",
visible,
});

export const setPythonClientModalVisibilityAction = (
visible: boolean,
): SetPythonClientModalVisibilityAction => ({
type: "SET_PYTHON_MODAL_VISIBILITY",
visible,
});
export const setShareModalVisibilityAction = (visible: boolean): SetShareModalVisibilityAction => ({
type: "SET_SHARE_MODAL_VISIBILITY",
visible,
Expand Down
4 changes: 4 additions & 0 deletions frontend/javascripts/oxalis/model/reducers/ui_reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ function UiReducer(state: OxalisState, action: Action): OxalisState {
return updateKey(state, "uiInformation", { showDownloadModal: action.visible });
}

case "SET_PYTHON_MODAL_VISIBILITY": {
return updateKey(state, "uiInformation", { showPythonClientModal: action.visible });
}

case "SET_SHARE_MODAL_VISIBILITY": {
return updateKey(state, "uiInformation", {
showShareModal: action.visible,
Expand Down
1 change: 1 addition & 0 deletions frontend/javascripts/oxalis/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ type UiInformation = {
readonly showDropzoneModal: boolean;
readonly showVersionRestore: boolean;
readonly showDownloadModal: boolean;
readonly showPythonClientModal: boolean;
readonly showShareModal: boolean;
readonly activeTool: AnnotationTool;
readonly storedLayouts: Record<string, any>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,13 @@ type Props = {
hasVolumeFallback: boolean;
};

function Hint({ children, style }: { children: React.ReactNode; style: React.CSSProperties }) {
export function Hint({
children,
style,
}: {
children: React.ReactNode;
style: React.CSSProperties;
}) {
return (
<div style={{ ...style, fontSize: 12, color: "var(--ant-text-secondary)" }}>{children}</div>
);
Expand All @@ -45,7 +51,7 @@ export async function copyToClipboard(code: string) {
Toast.success("Snippet copied to clipboard.");
}

function MoreInfoHint() {
export function MoreInfoHint() {
return (
<Hint
style={{
Expand All @@ -65,7 +71,7 @@ function MoreInfoHint() {
);
}

function CopyableCodeSnippet({ code, onCopy }: { code: string; onCopy?: () => void }) {
export function CopyableCodeSnippet({ code, onCopy }: { code: string; onCopy?: () => void }) {
return (
<pre>
<Button
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { Divider, Modal, Row, Typography } from "antd";
import React from "react";
import { useFetch, makeComponentLazy } from "libs/react_helpers";
import Toast from "libs/toast";
import messages from "messages";
import { getAuthToken } from "admin/admin_rest_api";
import { useSelector } from "react-redux";
import type { OxalisState } from "oxalis/store";
import { CopyableCodeSnippet, MoreInfoHint } from "./download_modal_view";
const { Paragraph, Text } = Typography;
type Props = {
isVisible: boolean;
onClose: () => void;
};

function _PythonClientModalView(props: Props): JSX.Element {
const { isVisible, onClose } = props;
const activeUser = useSelector((state: OxalisState) => state.activeUser);
const dataset = useSelector((state: OxalisState) => state.dataset);

const maybeShowWarning = () => (
<Row>
<Text
style={{
margin: "0 6px 12px",
}}
type="warning"
>
{activeUser != null
? messages["annotation.python_do_not_share"]
: messages["annotation.register_for_token"]}
</Text>
</Row>
);

const authToken = useFetch(
async () => {
if (activeUser != null) {
return getAuthToken();
}
return null;
},
"loading...",
[activeUser],
);
const wkInitSnippet = `import webknossos as wk

with wk.webknossos_context(token="${authToken || "<insert token here>"}"):
# Download the dataset.
dataset = wk.Dataset.download(
url="${window.location.origin}",
dataset="${dataset.name}"
organization_id="${dataset.owningOrganization}",
)
# Alternatively, directly open the dataset. Image data will be
# streamed when being accessed.
remote_dataset = wk.Dataset.open_remote(
url="${window.location.origin}",
dataset="${dataset.name}"
organization_id="${dataset.owningOrganization}",
)
`;

const alertTokenIsPrivate = () => {
Toast.warning(
"The clipboard contains private data. Do not share this information with anyone you do not trust!",
);
};

return (
<Modal
title="Python Client"
visible={isVisible}
width={800}
footer={null}
onCancel={onClose}
style={{ overflow: "visible" }}
>
<Row>
<Text
style={{
margin: "0 6px 12px",
}}
>
The following code snippets are suggestions to get you started quickly with the{" "}
<a href="https://docs.webknossos.org/webknossos-py/" target="_blank" rel="noreferrer">
webKnossos Python API
</a>
. To download and use this dataset in your Python project, simply copy and paste the code
snippets to your script.
</Text>
</Row>
<Divider
style={{
margin: "18px 0",
}}
>
Code Snippets
</Divider>
{maybeShowWarning()}
<Paragraph>
<CopyableCodeSnippet code="pip install webknossos" />
<CopyableCodeSnippet code={wkInitSnippet} onCopy={alertTokenIsPrivate} />
</Paragraph>
<Divider
style={{
margin: "18px 0",
}}
/>
<MoreInfoHint />
</Modal>
);
}

const PythonClientModalView = makeComponentLazy(_PythonClientModalView);
export default PythonClientModalView;
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,27 @@ import { CopyOutlined } from "@ant-design/icons";
import { Modal, Input, Button, Row, Col } from "antd";
import { useSelector } from "react-redux";
import React from "react";
import { makeComponentLazy } from "libs/react_helpers";
import messages from "messages";
import { OxalisState } from "oxalis/store";
import { useDatasetSharingToken, getUrl, copyUrlToClipboard } from "./share_modal_view";

const sharingActiveNode = false;

type Props = {
isVisible: boolean;
onOk: () => any;
};

export default function ShareViewDatasetModalView(props: Props) {
const { onOk } = props;
function _ShareViewDatasetModalView(props: Props) {
const { isVisible, onOk } = props;
const dataset = useSelector((state: OxalisState) => state.dataset);
const isShareModalOpen = useSelector((state: OxalisState) => state.uiInformation.showShareModal);
const sharingToken = useDatasetSharingToken(dataset);
const url = getUrl(sharingToken, !dataset.isPublic);
return (
<Modal
title="Share this Dataset"
visible={isShareModalOpen}
visible={isVisible}
width={800}
okText="Ok"
onOk={onOk}
Expand Down Expand Up @@ -71,3 +72,6 @@ export default function ShareViewDatasetModalView(props: Props) {
</Modal>
);
}

const ShareViewDatasetModalView = makeComponentLazy(_ShareViewDatasetModalView);
export default ShareViewDatasetModalView;
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ type StateProps = {
task: Task | null | undefined;
activeUser: APIUser | null | undefined;
hasTracing: boolean;
isShareModalOpen: boolean;
isDownloadModalOpen: boolean;
isShareModalOpen: boolean;
busyBlockingInfo: BusyBlockingInfo;
};
type Props = OwnProps & StateProps;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import React from "react";
import { useSelector } from "react-redux";
import { Dropdown, Menu } from "antd";
import { ShareAltOutlined, DownOutlined, CameraOutlined } from "@ant-design/icons";
import {
ShareAltOutlined,
DownOutlined,
CameraOutlined,
DownloadOutlined,
} from "@ant-design/icons";
import ButtonComponent from "oxalis/view/components/button_component";
import ShareViewDatasetModalView from "oxalis/view/action-bar/share_view_dataset_modal_view";
import { downloadScreenshot } from "oxalis/view/rendering_utils";
import { setShareModalVisibilityAction } from "oxalis/model/actions/ui_actions";
import Store from "oxalis/store";
import {
setPythonClientModalVisibilityAction,
setShareModalVisibilityAction,
} from "oxalis/model/actions/ui_actions";
import Store, { OxalisState } from "oxalis/store";
import PythonClientModalView from "./python_client_modal_view";

type Props = {
layoutMenu: React.ReactNode;
Expand All @@ -17,8 +27,21 @@ export const screenshotMenuItem = (
</Menu.Item>
);
export default function ViewDatasetActionsView(props: Props) {
const modal = (
<ShareViewDatasetModalView onOk={() => Store.dispatch(setShareModalVisibilityAction(false))} />
const isShareModalOpen = useSelector((state: OxalisState) => state.uiInformation.showShareModal);
const isPythonClientModalOpen = useSelector(
(state: OxalisState) => state.uiInformation.showPythonClientModal,
);
const shareDatasetModal = (
<ShareViewDatasetModalView
isVisible={isShareModalOpen}
onOk={() => Store.dispatch(setShareModalVisibilityAction(false))}
/>
);
const pythonClientModal = (
<PythonClientModalView
isVisible={isPythonClientModalOpen}
onClose={() => Store.dispatch(setPythonClientModalVisibilityAction(false))}
/>
);
const overlayMenu = (
<Menu>
Expand All @@ -30,6 +53,13 @@ export default function ViewDatasetActionsView(props: Props) {
Share
</Menu.Item>
{screenshotMenuItem}
<Menu.Item
key="python-client-button"
onClick={() => Store.dispatch(setPythonClientModalVisibilityAction(true))}
>
<DownloadOutlined />
Download
</Menu.Item>
{props.layoutMenu}
</Menu>
);
Expand All @@ -39,7 +69,8 @@ export default function ViewDatasetActionsView(props: Props) {
marginLeft: 10,
}}
>
{modal}
{shareDatasetModal}
{pythonClientModal}
<Dropdown overlay={overlayMenu} trigger={["click"]}>
<ButtonComponent
style={{
Expand Down