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

Allow to import NMLs which were created for different datasets #3716

Merged
merged 6 commits into from
Feb 4, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.md).
- The volume annotation brush tool will now automatically fill any enclosed areas if the brushed outline is closed in one stroke. [#3698](https://github.com/scalableminds/webknossos/pull/3698)
![brush-fill-4](https://user-images.githubusercontent.com/1702075/51846983-02d34480-231b-11e9-86f2-2d8c4b0c9bd0.gif)
- Statistics are now separated by organization, rather than showing the webKnossos instance’s totals. [#3663](https://github.com/scalableminds/webknossos/pull/3663)
- NML files can be imported into arbitrary datasets. Users will be asked to confirm the import process if the dataset of the NML differs from the currently active dataset. [#3716](https://github.com/scalableminds/webknossos/pull/3716)

### Fixed

Expand Down
24 changes: 24 additions & 0 deletions app/assets/javascripts/libs/async_confirm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// @flow
import { Modal } from "antd";

const { confirm } = Modal;

export async function binaryConfirm(title: string, content: string): Promise<boolean> {
return new Promise(resolve => {
confirm({
title,
content,
okText: "Yes",
okType: "danger",
cancelText: "No",
onOk() {
resolve(true);
},
onCancel() {
resolve(false);
},
});
});
}

export default {};
3 changes: 2 additions & 1 deletion app/assets/javascripts/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,8 @@ In order to restore the current window, a reload is necessary.`,
"nml.edge_with_same_source_target":
"NML contains <edge ...> with same source and target id: Edge",
"nml.tree_not_connected": "NML contains tree that is not fully connected: Tree with id",
"nml.different_dataset": "Imported NML was originally for a different dataset.",
"nml.different_dataset":
"At least one NML file was originally created for a different dataset. Are you sure you want to continue with the import?",
"merge.different_dataset":
"The merge cannot be executed, because the underlying datasets are not the same.",
"merge.volume_unsupported": "Merging is not supported for volume tracings.",
Expand Down
12 changes: 6 additions & 6 deletions app/assets/javascripts/oxalis/model/helpers/nml_helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { getPosition, getRotation } from "oxalis/model/accessors/flycam_accessor
import Date from "libs/date";
import DiffableMap from "libs/diffable_map";
import EdgeCollection from "oxalis/model/edge_collection";
import Store, {
import {
type NodeMap,
type OxalisState,
type SkeletonTracing,
Expand Down Expand Up @@ -428,7 +428,7 @@ function wrapInNewGroup(
export function parseNml(
nmlString: string,
wrappingGroupName?: ?string,
): Promise<{ trees: TreeMap, treeGroups: Array<TreeGroup> }> {
): Promise<{ trees: TreeMap, treeGroups: Array<TreeGroup>, datasetName: ?string }> {
return new Promise((resolve, reject) => {
const parser = new Saxophone();

Expand All @@ -440,14 +440,13 @@ export function parseNml(
let currentTree: ?Tree = null;
let currentGroup: ?TreeGroup = null;
const groupIdToParent: { [number]: ?TreeGroup } = {};
let datasetName = null;
parser
.on("tagopen", node => {
const attr = Saxophone.parseAttrs(node.attrs);
switch (node.name) {
case "experiment": {
if (attr.name !== Store.getState().dataset.name) {
throw new NmlParseError(messages["nml.different_dataset"]);
}
datasetName = attr.name;
break;
}
case "thing": {
Expand Down Expand Up @@ -619,9 +618,10 @@ export function parseNml(
resolve({
trees: wrappedTrees,
treeGroups: wrappedTreeGroups,
datasetName,
});
} else {
resolve({ trees, treeGroups });
resolve({ trees, treeGroups, datasetName });
}
})
.on("error", reject);
Expand Down
42 changes: 30 additions & 12 deletions app/assets/javascripts/oxalis/view/right-menu/trees_tab_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@
* list_tree_view.js
* @flow
*/
import _ from "lodash";
import { Alert, Button, Dropdown, Input, Menu, Icon, Spin, Modal, Tooltip } from "antd";
import type { Dispatch } from "redux";
import { connect } from "react-redux";
import { saveAs } from "file-saver";
import * as React from "react";
import _ from "lodash";

import { binaryConfirm } from "libs/async_confirm";
import {
createGroupToTreesMap,
callDeep,
MISSING_GROUP_ID,
} from "oxalis/view/right-menu/tree_hierarchy_view_helpers";
import { getActiveTree, getActiveGroup } from "oxalis/model/accessors/skeletontracing_accessor";
import { getBuildInfo } from "admin/admin_rest_api";
import { readFileAsText } from "libs/read_file";
Expand All @@ -31,12 +37,6 @@ import {
setTreeGroupsAction,
addTreesAndGroupsAction,
} from "oxalis/model/actions/skeletontracing_actions";
import messages from "messages";
import {
createGroupToTreesMap,
callDeep,
MISSING_GROUP_ID,
} from "oxalis/view/right-menu/tree_hierarchy_view_helpers";
import { updateUserSettingAction } from "oxalis/model/actions/settings_actions";
import ButtonComponent from "oxalis/view/components/button_component";
import InputComponent from "oxalis/view/components/input_component";
Expand All @@ -51,8 +51,10 @@ import Toast from "libs/toast";
import TreeHierarchyView from "oxalis/view/right-menu/tree_hierarchy_view";
import * as Utils from "libs/utils";
import api from "oxalis/api/internal_api";
import SearchPopover from "./search_popover";
import messages from "messages";

import DeleteGroupModalView from "./delete_group_modal_view";
import SearchPopover from "./search_popover";

const ButtonGroup = Button.Group;
const InputGroup = Input.Group;
Expand Down Expand Up @@ -91,15 +93,18 @@ type State = {

export async function importNmls(files: Array<File>, createGroupForEachFile: boolean) {
try {
const { successes: importActions, errors } = await Utils.promiseAllWithErrors(
const { successes: importActionsWithDatasetNames, errors } = await Utils.promiseAllWithErrors(
files.map(async file => {
const nmlString = await readFileAsText(file);
try {
const { trees, treeGroups } = await parseNml(
const { trees, treeGroups, datasetName } = await parseNml(
nmlString,
createGroupForEachFile ? file.name : null,
);
return addTreesAndGroupsAction(trees, treeGroups);
return {
importAction: addTreesAndGroupsAction(trees, treeGroups),
datasetName,
};
} catch (e) {
throw new Error(`"${file.name}" could not be parsed. ${e.message}`);
}
Expand All @@ -110,10 +115,23 @@ export async function importNmls(files: Array<File>, createGroupForEachFile: boo
throw errors;
}

const currentDatasetName = Store.getState().dataset.name;
const doDatasetNamesDiffer = importActionsWithDatasetNames
.map(el => el.datasetName)
.some(name => name !== "" && name != null && name !== currentDatasetName);
if (doDatasetNamesDiffer) {
const shouldImport = await binaryConfirm("Are you sure?", messages["nml.different_dataset"]);
if (!shouldImport) {
return;
}
}

// Dispatch the actual actions as the very last step, so that
// not a single store mutation happens if something above throws
// an error
importActions.forEach(action => Store.dispatch(action));
importActionsWithDatasetNames
.map(el => el.importAction)
.forEach(action => Store.dispatch(action));
} catch (e) {
(Array.isArray(e) ? e : [e]).forEach(err => Toast.error(err.message));
}
Expand Down