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 download NMLs of older versions for read-only tracings #3660

Merged
merged 10 commits into from
Jan 23, 2019
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.md).
### Added

- Added the possibility to disable that the current layout is saved automatically when changing it. Instead, the layout can be saved explicitly. [#3620](https://github.com/scalableminds/webknossos/pull/3620)
- Added the possibility to open the version restore view for read-only tracings. Older versions can be previewed and be downloaded as NML. [#3660](https://github.com/scalableminds/webknossos/pull/3660)

### Changed

Expand Down
47 changes: 32 additions & 15 deletions app/assets/javascripts/admin/admin_rest_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import _ from "lodash";
import {
type APIActiveUser,
type APIAnnotation,
type APIAnnotationTypeCompact,
type APIAnnotationCompact,
type APIAnnotationWithTask,
type APIBuildInfo,
type APIDataSource,
Expand All @@ -30,8 +30,8 @@ import {
type APITimeInterval,
type APITimeTracking,
type APITracingStore,
type APITracingType,
APITracingTypeEnum,
type APIAnnotationType,
APIAnnotationTypeEnum,
type APIUpdateActionBatch,
type APIUser,
type APIUserLoggedTime,
Expand Down Expand Up @@ -422,7 +422,7 @@ export async function updateTask(taskId: string, task: NewTask): Promise<APITask
}

export function finishTask(annotationId: string): Promise<APIAnnotation> {
return finishAnnotation(annotationId, APITracingTypeEnum.Task);
return finishAnnotation(annotationId, APIAnnotationTypeEnum.Task);
}

export function transferTask(annotationId: string, userId: string): Promise<APIAnnotation> {
Expand Down Expand Up @@ -454,7 +454,7 @@ export async function getUsersWithActiveTasks(projectName: string): Promise<Arra
export function getCompactAnnotations(
isFinished: boolean,
pageNumber: number = 0,
): Promise<Array<APIAnnotationTypeCompact>> {
): Promise<Array<APIAnnotationCompact>> {
return Request.receiveJSON(
`/api/user/annotations?isFinished=${isFinished.toString()}&pageNumber=${pageNumber}`,
);
Expand All @@ -464,15 +464,15 @@ export function getCompactAnnotationsForUser(
userId: string,
isFinished: boolean,
pageNumber: number = 0,
): Promise<Array<APIAnnotationTypeCompact>> {
): Promise<Array<APIAnnotationCompact>> {
return Request.receiveJSON(
`/api/users/${userId}/annotations?isFinished=${isFinished.toString()}&pageNumber=${pageNumber}`,
);
}

export function reOpenAnnotation(
annotationId: string,
annotationType: APITracingType,
annotationType: APIAnnotationType,
): Promise<APIAnnotation> {
return Request.receiveJSON(`/api/annotations/${annotationType}/${annotationId}/reopen`, {
method: "PATCH",
Expand All @@ -488,7 +488,7 @@ export type EditableAnnotation = {

export function editAnnotation(
annotationId: string,
annotationType: APITracingType,
annotationType: APIAnnotationType,
data: $Shape<EditableAnnotation>,
): Promise<void> {
return Request.sendJSONReceiveJSON(`/api/annotations/${annotationType}/${annotationId}/edit`, {
Expand All @@ -499,7 +499,7 @@ export function editAnnotation(

export function finishAnnotation(
annotationId: string,
annotationType: APITracingType,
annotationType: APIAnnotationType,
): Promise<APIAnnotation> {
return Request.receiveJSON(`/api/annotations/${annotationType}/${annotationId}/finish`, {
method: "PATCH",
Expand All @@ -508,7 +508,7 @@ export function finishAnnotation(

export function resetAnnotation(
annotationId: string,
annotationType: APITracingType,
annotationType: APIAnnotationType,
): Promise<APIAnnotation> {
return Request.receiveJSON(`/api/annotations/${annotationType}/${annotationId}/reset`, {
method: "PUT",
Expand All @@ -517,7 +517,7 @@ export function resetAnnotation(

export function deleteAnnotation(
annotationId: string,
annotationType: APITracingType,
annotationType: APIAnnotationType,
): Promise<{ messages: Array<Message> }> {
return Request.receiveJSON(`/api/annotations/${annotationType}/${annotationId}`, {
method: "DELETE",
Expand All @@ -537,17 +537,17 @@ export function finishAllAnnotations(

export function copyAnnotationToUserAccount(
annotationId: string,
tracingType: APITracingType,
annotationType: APIAnnotationType,
): Promise<APIAnnotation> {
const url = `/api/annotations/${tracingType}/${annotationId}/duplicate`;
const url = `/api/annotations/${annotationType}/${annotationId}/duplicate`;
return Request.receiveJSON(url, { method: "POST" });
}

export function getAnnotationInformation(
annotationId: string,
tracingType: APITracingType,
annotationType: APIAnnotationType,
): Promise<APIAnnotation> {
const infoUrl = `/api/annotations/${tracingType}/${annotationId}/info`;
const infoUrl = `/api/annotations/${annotationType}/${annotationId}/info`;
return Request.receiveJSON(infoUrl);
}

Expand Down Expand Up @@ -625,6 +625,23 @@ export function convertToHybridTracing(annotationId: string): Promise<void> {
});
}

export async function downloadNml(
annotationId: string,
annotationType: APIAnnotationType,
versions?: Versions = {},
) {
const possibleVersionString = Object.entries(versions)
// $FlowFixMe Flow returns val as mixed here due to the use of Object.entries
.map(([key, val]) => `${key}Version=${val}`)
.join("&");
const win = window.open("about:blank", "_blank");
win.document.body.innerHTML = messages["download.wait"];

const downloadUrl = `/api/annotations/${annotationType}/${annotationId}/download?${possibleVersionString}`;
win.location.href = downloadUrl;
win.document.body.innerHTML = messages["download.close_window"];
}

// ### Datasets
export async function getDatasets(): Promise<Array<APIMaybeUnimportedDataset>> {
const datasets = await Request.receiveJSON("/api/datasets");
Expand Down
14 changes: 7 additions & 7 deletions app/assets/javascripts/admin/api_flow_types.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ export type APISettings = {|
+somaClickingAllowed: boolean,
|};

export const APITracingTypeEnum = Enum.make({
export const APIAnnotationTypeEnum = Enum.make({
Explorational: "Explorational",
Task: "Task",
View: "View",
Expand All @@ -183,7 +183,7 @@ export const APITracingTypeEnum = Enum.make({
CompoundTaskType: "CompoundTaskType",
});

export type APITracingType = $Keys<typeof APITracingTypeEnum>;
export type APIAnnotationType = $Keys<typeof APIAnnotationTypeEnum>;

export type APITaskType = {
+id: string,
Expand Down Expand Up @@ -266,7 +266,7 @@ export type APITask = {
+directLinks?: Array<string>,
};

export type APIAnnotationTypeCompact = {
export type APIAnnotationCompact = {
+tracing: {
+skeleton: ?string,
+volume: ?string,
Expand All @@ -283,7 +283,7 @@ export type APIAnnotationTypeCompact = {
+stats: SkeletonTracingStats | {||},
+tags: Array<string>,
+tracingTime: ?number,
+typ: APITracingType,
+typ: APIAnnotationType,
};

export type LocalMeshMetaData = {|
Expand All @@ -304,7 +304,7 @@ export type MeshMetaData = {|
...RemoteMeshMetaData,
|};

type APIAnnotationTypeBase = APIAnnotationTypeCompact & {
type APIAnnotationBase = APIAnnotationCompact & {
+dataStore: APIDataStore,
+tracingStore: APITracingStore,
+restrictions: APIRestrictions,
Expand All @@ -313,11 +313,11 @@ type APIAnnotationTypeBase = APIAnnotationTypeCompact & {
+meshes: Array<MeshMetaData>,
};

export type APIAnnotation = APIAnnotationTypeBase & {
export type APIAnnotation = APIAnnotationBase & {
+task: ?APITask,
};

export type APIAnnotationWithTask = APIAnnotationTypeBase & {
export type APIAnnotationWithTask = APIAnnotationBase & {
+task: APITask,
};

Expand Down
28 changes: 14 additions & 14 deletions app/assets/javascripts/dashboard/explorative_annotations_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import * as React from "react";
import _ from "lodash";
import update from "immutability-helper";

import type { APIAnnotationTypeCompact } from "admin/api_flow_types";
import type { APIAnnotationCompact } from "admin/api_flow_types";
import { AnnotationContentTypes } from "oxalis/constants";
import { AsyncLink } from "components/async_clickables";
import {
Expand All @@ -36,11 +36,11 @@ import { trackAction } from "oxalis/model/helpers/analytics";
const { Column } = Table;
const { Search } = Input;

const typeHint: APIAnnotationTypeCompact[] = [];
const typeHint: APIAnnotationCompact[] = [];
const pageLength: number = 1000;

export type TracingModeState = {
tracings: Array<APIAnnotationTypeCompact>,
tracings: Array<APIAnnotationCompact>,
lastLoadedPage: number,
loadedAllTracings: boolean,
};
Expand Down Expand Up @@ -178,7 +178,7 @@ class ExplorativeAnnotationsView extends React.PureComponent<Props, State> {
);
};

finishOrReopenTracing = async (type: "finish" | "reopen", tracing: APIAnnotationTypeCompact) => {
finishOrReopenTracing = async (type: "finish" | "reopen", tracing: APIAnnotationCompact) => {
const newTracing =
type === "finish"
? await finishAnnotation(tracing.id, tracing.typ)
Expand All @@ -205,7 +205,7 @@ class ExplorativeAnnotationsView extends React.PureComponent<Props, State> {
}
};

renderActions = (tracing: APIAnnotationTypeCompact) => {
renderActions = (tracing: APIAnnotationCompact) => {
if (tracing.typ !== "Explorational") {
return null;
}
Expand Down Expand Up @@ -240,15 +240,15 @@ class ExplorativeAnnotationsView extends React.PureComponent<Props, State> {
}
};

getCurrentTracings(): Array<APIAnnotationTypeCompact> {
getCurrentTracings(): Array<APIAnnotationCompact> {
return this.getCurrentModeState().tracings;
}

handleSearch = (event: SyntheticInputEvent<>): void => {
this.setState({ searchQuery: event.target.value });
};

renameTracing(tracing: APIAnnotationTypeCompact, name: string) {
renameTracing(tracing: APIAnnotationCompact, name: string) {
const tracings = this.getCurrentTracings();

const newTracings = tracings.map(currentTracing => {
Expand Down Expand Up @@ -310,7 +310,7 @@ class ExplorativeAnnotationsView extends React.PureComponent<Props, State> {
};

editTagFromAnnotation = (
annotation: APIAnnotationTypeCompact,
annotation: APIAnnotationCompact,
shouldAddTag: boolean,
tag: string,
event: SyntheticInputEvent<>,
Expand Down Expand Up @@ -359,7 +359,7 @@ class ExplorativeAnnotationsView extends React.PureComponent<Props, State> {
);
}

renderNameWithDescription(tracing: APIAnnotationTypeCompact) {
renderNameWithDescription(tracing: APIAnnotationCompact) {
const hasDescription = tracing.description !== "";
const markdownDescription = (
<div style={{ maxWidth: 400 }}>
Expand Down Expand Up @@ -403,21 +403,21 @@ class ExplorativeAnnotationsView extends React.PureComponent<Props, State> {
<Column
title="ID"
dataIndex="id"
render={(__, tracing: APIAnnotationTypeCompact) => formatHash(tracing.id)}
render={(__, tracing: APIAnnotationCompact) => formatHash(tracing.id)}
sorter={Utils.localeCompareBy(typeHint, annotation => annotation.id)}
className="monospace-id"
/>
<Column
title="Name"
dataIndex="name"
sorter={Utils.localeCompareBy(typeHint, annotation => annotation.name)}
render={(name: string, tracing: APIAnnotationTypeCompact) =>
render={(name: string, tracing: APIAnnotationCompact) =>
this.renderNameWithDescription(tracing)
}
/>
<Column
title="Stats"
render={(__, annotation: APIAnnotationTypeCompact) =>
render={(__, annotation: APIAnnotationCompact) =>
// Flow doesn't recognize that stats must contain the nodeCount if the treeCount is != null
annotation.stats.treeCount != null &&
annotation.stats.nodeCount != null &&
Expand Down Expand Up @@ -446,7 +446,7 @@ class ExplorativeAnnotationsView extends React.PureComponent<Props, State> {
title="Tags"
dataIndex="tags"
width={500}
render={(tags: Array<string>, annotation: APIAnnotationTypeCompact) => (
render={(tags: Array<string>, annotation: APIAnnotationCompact) => (
<div>
{tags.map(tag => (
<Tag
Expand Down Expand Up @@ -481,7 +481,7 @@ class ExplorativeAnnotationsView extends React.PureComponent<Props, State> {
title="Actions"
className="nowrap"
key="action"
render={(__, tracing: APIAnnotationTypeCompact) => this.renderActions(tracing)}
render={(__, tracing: APIAnnotationCompact) => this.renderActions(tracing)}
/>
</Table>
);
Expand Down
10 changes: 5 additions & 5 deletions app/assets/javascripts/oxalis/api/api_latest.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import type {
Tracing,
SkeletonTracing,
VolumeTracing,
TracingTypeTracing,
AnnotationType,
Mapping,
TreeGroupTypeFlat,
} from "oxalis/store";
Expand Down Expand Up @@ -368,11 +368,11 @@ class TracingApi {

this.isFinishing = true;
const state = Store.getState();
const { tracingType, annotationId } = state.tracing;
const { annotationType, annotationId } = state.tracing;
const { task } = state;

await Model.save();
await finishAnnotation(annotationId, tracingType);
await finishAnnotation(annotationId, annotationType);
try {
const annotation = await requestTask();

Expand Down Expand Up @@ -409,7 +409,7 @@ class TracingApi {
*
*/
async restart(
newTracingType: TracingTypeTracing,
newAnnotationType: AnnotationType,
newAnnotationId: string,
newControlMode: ControlMode,
versions?: Versions,
Expand All @@ -420,7 +420,7 @@ class TracingApi {
Store.dispatch(restartSagaAction());
UrlManager.reset();
await Model.fetch(
newTracingType,
newAnnotationType,
{ annotationId: newAnnotationId, type: newControlMode },
false,
versions,
Expand Down
Loading