diff --git a/CHANGELOG.md b/CHANGELOG.md index 459f0789861..46685b3a30a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,9 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.md). - Added support for duplicate dataset names for different organizations [#3137](https://github.com/scalableminds/webknossos/pull/3137) - Extended the version restore view and added a view to restore older versions of a volume tracing. Access it through the dropdown next to the Save button. [#3349](https://github.com/scalableminds/webknossos/pull/3349) - Added support to watch additional dataset directories, automatically creating symbolic links to the main directory [#3330](https://github.com/scalableminds/webknossos/pull/3330) +- Added a button to the users list view that revokes admin rights from all selected users. [#3378](https://github.com/scalableminds/webknossos/pull/3378) - A User can now have multiple layouts for tracing views. [#3299](https://github.com/scalableminds/webknossos/pull/3299) +- The info tab in tracing views now displays the extent of the current dataset. [#3371](https://github.com/scalableminds/webknossos/pull/3371). ### Changed diff --git a/app/assets/javascripts/admin/task/task_annotation_view.js b/app/assets/javascripts/admin/task/task_annotation_view.js index ccdf06147c3..df7554053e4 100644 --- a/app/assets/javascripts/admin/task/task_annotation_view.js +++ b/app/assets/javascripts/admin/task/task_annotation_view.js @@ -113,41 +113,32 @@ class TaskAnnotationView extends React.PureComponent {label} - - - this.setState({ currentAnnotation: annotation, isTransferModalVisible: true }) - } - > - Transfer - + + this.setState({ currentAnnotation: annotation, isTransferModalVisible: true }) + } + > + Transfer Download - - this.resetAnnotation(annotation)}> - Reset - + this.resetAnnotation(annotation)}> + Reset - - this.deleteAnnotation(annotation)}> - Reset and Cancel - + this.deleteAnnotation(annotation)}> + Reset and Cancel {annotation.state === "Finished" ? ( - - this.reOpenAnnotation(annotation)}> - Reopen - + this.reOpenAnnotation(annotation)}> + Reopen ) : ( - - this.finishAnnotation(annotation)}> - Finish - + this.finishAnnotation(annotation)}> + Finish )} diff --git a/app/assets/javascripts/admin/user/user_list_view.js b/app/assets/javascripts/admin/user/user_list_view.js index df9a3fb7aac..f1cde03c190 100644 --- a/app/assets/javascripts/admin/user/user_list_view.js +++ b/app/assets/javascripts/admin/user/user_list_view.js @@ -170,11 +170,11 @@ class UserListView extends React.PureComponent { }); }; - grantAdminRights = async () => { + setAdminRightsTo = async (isAdmin: boolean) => { if (this.props.activeUser.isAdmin) { const newUserPromises = this.state.users.map(user => { if (this.state.selectedUserIds.includes(user.id)) { - const newUser = Object.assign({}, user, { isAdmin: true }); + const newUser = Object.assign({}, user, { isAdmin }); return updateUser(newUser); } @@ -305,22 +305,40 @@ class UserListView extends React.PureComponent { Change Experience {this.props.activeUser.isAdmin ? ( - + + + + ) : null} { const menu = ( - - this.createTracing(dataset, "volume", true)} - title="Create volume tracing" - > + this.createTracing(dataset, "volume", true)}> + Use Existing Segmentation Layer - - this.createTracing(dataset, "volume", false)} - title="Create volume tracing" - > + this.createTracing(dataset, "volume", false)}> + Use a New Segmentation Layer diff --git a/app/assets/javascripts/libs/utils.js b/app/assets/javascripts/libs/utils.js index 2e92be8dcc6..3a103267dd2 100644 --- a/app/assets/javascripts/libs/utils.js +++ b/app/assets/javascripts/libs/utils.js @@ -5,6 +5,7 @@ import Maybe from "data.maybe"; import window, { document, location } from "libs/window"; import naturalSort from "javascript-natural-sort"; import type { APIUser } from "admin/api_flow_types"; +import type { BoundingBoxObject } from "oxalis/store"; export type Comparator = (T, T) => -1 | 0 | 1; type UrlParams = { [key: string]: string }; @@ -129,6 +130,24 @@ export function computeArrayFromBoundingBox(bb: ?BoundingBoxType): ?Vector6 { : null; } +export function aggregateBoundingBox(boundingBoxes: Array): BoundingBoxType { + const allCoordinates = [0, 1, 2].map(index => + boundingBoxes.map(box => box.topLeft[index]).concat( + boundingBoxes.map(box => { + const bottomRight = [ + box.topLeft[0] + box.width, + box.topLeft[1] + box.height, + box.topLeft[2] + box.depth, + ]; + return bottomRight[index]; + }), + ), + ); + const min = (([0, 1, 2].map(index => Math.min(...allCoordinates[index])): any): Vector3); + const max = (([0, 1, 2].map(index => Math.max(...allCoordinates[index])): any): Vector3); + return { min, max }; +} + export function compareBy( collectionForTypeInference: Array, // this parameter is only used let flow infer the used type selector: T => number, diff --git a/app/assets/javascripts/messages.js b/app/assets/javascripts/messages.js index 5a73c137590..5c092c8a222 100644 --- a/app/assets/javascripts/messages.js +++ b/app/assets/javascripts/messages.js @@ -185,6 +185,10 @@ In order to restore the current window, a reload is necessary.`, "users.grant_admin_rights": _.template( "You are about to grant admin privileges to <%- numUsers %> user(s) giving them access to all teams, datasets and annotations. Do you want to proceed?", ), + "users.revoke_admin_rights_title": "Do you really want to revoke admin rights?", + "users.revoke_admin_rights": _.template( + "You are about to revoke admin privileges from <%- numUsers %> user(s). Do you want to proceed?", + ), "users.change_email_title": "Do you really want to change the email?", "users.change_email": _.template( "Do you really want to change the email to '<%- newEmail %>' ? The corresponding user will be logged out and unsaved changes might be lost.", diff --git a/app/assets/javascripts/oxalis/controller/scene_controller.js b/app/assets/javascripts/oxalis/controller/scene_controller.js index b441eed91ab..80138f7b7ac 100644 --- a/app/assets/javascripts/oxalis/controller/scene_controller.js +++ b/app/assets/javascripts/oxalis/controller/scene_controller.js @@ -24,12 +24,17 @@ import Skeleton from "oxalis/geometries/skeleton"; import Cube from "oxalis/geometries/cube"; import ContourGeometry from "oxalis/geometries/contourgeometry"; import Dimensions from "oxalis/model/dimensions"; -import { OrthoViews, OrthoViewValues, OrthoViewValuesWithoutTDView } from "oxalis/constants"; +import constants, { + OrthoViews, + OrthoViewValues, + OrthoViewValuesWithoutTDView, +} from "oxalis/constants"; import type { Vector3, OrthoView, OrthoViewMap, BoundingBoxType } from "oxalis/constants"; import { listenToStoreProperty } from "oxalis/model/helpers/listener_helpers"; import { getRenderer } from "oxalis/controller/renderer"; import ArbitraryPlane from "oxalis/geometries/arbitrary_plane"; import { getSomeTracing } from "oxalis/model/accessors/tracing_accessor"; +import window from "libs/window"; const CUBE_COLOR = 0x999999; @@ -80,6 +85,31 @@ class SceneController { this.rootGroup.scale.copy(new THREE.Vector3(...Store.getState().dataset.dataSource.scale)); // Add scene to the group, all Geometries are then added to group this.scene.add(this.rootGroup); + + this.setupDebuggingMethods(); + } + + setupDebuggingMethods() { + // These methods are attached to window, since we would run into circular import errors + // otherwise. + window.addBucketMesh = (position: Vector3, zoomStep: number, optColor?: string) => { + const bucketExtent = constants.BUCKET_WIDTH * 2 ** zoomStep; + const bucketSize = [bucketExtent, bucketExtent, bucketExtent]; + const boxGeometry = new THREE.BoxGeometry(...bucketSize); + const edgesGeometry = new THREE.EdgesGeometry(boxGeometry); + const material = new THREE.LineBasicMaterial({ + color: optColor || (zoomStep === 0 ? 0xff00ff : 0x00ffff), + linewidth: 1, + }); + const cube = new THREE.LineSegments(edgesGeometry, material); + cube.position.x = position[0] + bucketSize[0] / 2; + cube.position.y = position[1] + bucketSize[1] / 2; + cube.position.z = position[2] + bucketSize[2] / 2; + this.rootNode.add(cube); + return cube; + }; + + window.removeBucketMesh = (mesh: THREE.LineSegments) => this.rootNode.remove(mesh); } addSTL(stlBuffer: ArrayBuffer): void { diff --git a/app/assets/javascripts/oxalis/model/bucket_data_handling/bucket.js b/app/assets/javascripts/oxalis/model/bucket_data_handling/bucket.js index 3c7b17db351..ffeeec8cdd7 100644 --- a/app/assets/javascripts/oxalis/model/bucket_data_handling/bucket.js +++ b/app/assets/javascripts/oxalis/model/bucket_data_handling/bucket.js @@ -6,11 +6,15 @@ import _ from "lodash"; import BackboneEvents from "backbone-events-standalone"; import type { Vector3, Vector4 } from "oxalis/constants"; +import * as THREE from "three"; import constants from "oxalis/constants"; import TemporalBucketManager from "oxalis/model/bucket_data_handling/temporal_bucket_manager"; import * as Utils from "libs/utils"; import window from "libs/window"; import Toast from "libs/toast"; +import { getResolutions } from "oxalis/model/accessors/dataset_accessor"; +import Store from "oxalis/store"; +import { bucketPositionToGlobalAddress } from "oxalis/model/helpers/position_converter"; export const BucketStateEnum = { UNREQUESTED: "UNREQUESTED", @@ -31,6 +35,8 @@ export class DataBucket { BIT_DEPTH: number; BUCKET_LENGTH: number; BYTE_OFFSET: number; + visualizedMesh: ?Object; + visualizationColor: number; state: BucketStateEnumType; dirty: boolean; @@ -322,6 +328,37 @@ export class DataBucket { } } } + + // The following three methods can be used for debugging purposes. + // The bucket will be rendered in the 3D scene as a wireframe geometry. + visualize() { + if (this.visualizedMesh != null) { + return; + } + if (this.zoomedAddress[3] === 0 || this.zoomedAddress[3] === 1) { + const resolutions = getResolutions(Store.getState().dataset); + this.visualizedMesh = window.addBucketMesh( + bucketPositionToGlobalAddress(this.zoomedAddress, resolutions), + this.zoomedAddress[3], + this.visualizationColor, + ); + } + } + + unvisualize() { + if (this.visualizedMesh != null) { + window.removeBucketMesh(this.visualizedMesh); + this.visualizedMesh = null; + } + } + + setVisualizationColor(colorDescriptor: string | number) { + const color = new THREE.Color(colorDescriptor); + this.visualizationColor = color; + if (this.visualizedMesh != null) { + this.visualizedMesh.material.color = color; + } + } } export class NullBucket { diff --git a/app/assets/javascripts/oxalis/model/bucket_data_handling/texture_bucket_manager.js b/app/assets/javascripts/oxalis/model/bucket_data_handling/texture_bucket_manager.js index a947b096b97..a82a722d3ba 100644 --- a/app/assets/javascripts/oxalis/model/bucket_data_handling/texture_bucket_manager.js +++ b/app/assets/javascripts/oxalis/model/bucket_data_handling/texture_bucket_manager.js @@ -182,6 +182,7 @@ export default class TextureBucketManager { bucketHeightInTexture, ); this.committedBucketSet.add(bucket); + window.needsRerender = true; this.isRefreshBufferOutOfDate = true; } diff --git a/app/assets/javascripts/oxalis/view/action-bar/tracing_actions_view.js b/app/assets/javascripts/oxalis/view/action-bar/tracing_actions_view.js index 534750b2db8..0e0e5a862e8 100644 --- a/app/assets/javascripts/oxalis/view/action-bar/tracing_actions_view.js +++ b/app/assets/javascripts/oxalis/view/action-bar/tracing_actions_view.js @@ -82,28 +82,26 @@ export const ResetLayoutItem = (props: ResetLayoutItemProps) => { return ( onSelectLayout(layout)} className={ isSelectedLayout ? "selected-layout-item bullet-point-less-li" : "bullet-point-less-li" } > - onSelectLayout(layout)} - style={{ minWidth: "100%", minHeight: "auto", display: "inline-block" }} - > -
+ +
onSelectLayout(layout)}> {layout} - {isSelectedLayout ? ( - - ) : ( - - onDeleteLayout(layout)} - /> - - )}
+ {isSelectedLayout ? ( + + ) : ( + + onDeleteLayout(layout)} + /> + + )}
); @@ -124,12 +122,8 @@ export const ResetLayoutItem = (props: ResetLayoutItemProps) => { } > - -
Reset Layout
-
- -
Add a new Layout
-
+ Reset Layout + Add a new Layout { Store.dispatch(redoAction()); }; - handleCopyToAccount = async (event: SyntheticInputEvent<>) => { - event.target.blur(); + handleCopyToAccount = async () => { const newAnnotation = await copyAnnotationToUserAccount( this.props.annotationId, this.props.tracingType, @@ -183,8 +176,7 @@ class TracingActionsView extends PureComponent { location.href = `/annotations/Explorational/${newAnnotation.id}`; }; - handleFinish = async (event: SyntheticInputEvent<>) => { - event.target.blur(); + handleFinish = async () => { await this.handleSave(); Modal.confirm({ @@ -205,8 +197,7 @@ class TracingActionsView extends PureComponent { this.setState({ isShareModalOpen: false }); }; - handleDownload = async (event: SyntheticInputEvent<>) => { - event.target.blur(); + handleDownload = async () => { const win = window.open("about:blank", "_blank"); win.document.body.innerHTML = messages["download.wait"]; await this.handleSave(); @@ -218,8 +209,7 @@ class TracingActionsView extends PureComponent { win.document.body.innerHTML = messages["download.close_window"]; }; - handleFinishAndGetNextTask = async (event: SyntheticInputEvent<>) => { - event.target.blur(); + handleFinishAndGetNextTask = async () => { api.tracing.finishAndGetNextTask(); }; @@ -283,30 +273,24 @@ class TracingActionsView extends PureComponent { const modals = []; if (restrictions.allowFinish) { elements.push( - -
- - {archiveButtonText} -
+ + + {archiveButtonText} , ); } if (restrictions.allowDownload) { elements.push( - -
- - Download -
+ + + Download , ); } elements.push( - -
- - Share -
+ + + Share , ); modals.push( @@ -317,11 +301,9 @@ class TracingActionsView extends PureComponent { />, ); elements.push( - -
- - Add Script -
+ + + Add Script , ); modals.push( @@ -334,11 +316,9 @@ class TracingActionsView extends PureComponent { if (isSkeletonMode && this.props.activeUser != null) { elements.push( - -
- - Merge Tracing -
+ + + Merge Tracing , ); modals.push( diff --git a/app/assets/javascripts/oxalis/view/right-menu/dataset_info_tab_view.js b/app/assets/javascripts/oxalis/view/right-menu/dataset_info_tab_view.js index f3c0e9cdc43..c83549b4939 100644 --- a/app/assets/javascripts/oxalis/view/right-menu/dataset_info_tab_view.js +++ b/app/assets/javascripts/oxalis/view/right-menu/dataset_info_tab_view.js @@ -10,6 +10,7 @@ import { getStats } from "oxalis/model/accessors/skeletontracing_accessor"; import { getPlaneScalingFactor } from "oxalis/model/accessors/flycam_accessor"; import Store from "oxalis/store"; import { formatScale } from "libs/format_utils"; +import { aggregateBoundingBox } from "libs/utils"; import { setAnnotationNameAction, setAnnotationDescriptionAction, @@ -89,7 +90,7 @@ export function calculateZoomLevel(flycam: Flycam, dataset: APIDataset): number return zoom * width * baseVoxel; } -export function formatZoomLevel(zoomLevel: number): string { +export function formatNumberToLength(zoomLevel: number): string { if (zoomLevel < 1000) { return `${zoomLevel.toFixed(0)} nm`; } else if (zoomLevel < 1000000) { @@ -99,6 +100,36 @@ export function formatZoomLevel(zoomLevel: number): string { } } +function getDatasetExtentInVoxel(dataset: APIDataset) { + const datasetLayers = dataset.dataSource.dataLayers; + const allBoundingBoxes = datasetLayers.map(layer => layer.boundingBox); + const unifiedBoundingBoxes = aggregateBoundingBox(allBoundingBoxes); + const { min, max } = unifiedBoundingBoxes; + const extent = { + width: max[0] - min[0], + height: max[1] - min[1], + depth: max[2] - min[2], + }; + return extent; +} + +function getDatasetExtentInLength(dataset: APIDataset) { + const extentInVoxel = getDatasetExtentInVoxel(dataset); + const scale = dataset.dataSource.scale; + const extent = { + width: extentInVoxel.width * scale[0], + height: extentInVoxel.height * scale[1], + depth: extentInVoxel.depth * scale[2], + }; + return extent; +} + +function formatExtentWithLength(extent: Object, formattingFunction: number => string) { + return `${formattingFunction(extent.width)} x ${formattingFunction( + extent.height, + )} x ${formattingFunction(extent.depth)}`; +} + class DatasetInfoTabView extends React.PureComponent { setAnnotationName = (newName: string) => { this.props.setAnnotationName(newName); @@ -226,14 +257,29 @@ class DatasetInfoTabView extends React.PureComponent { Store.getState().temporaryConfiguration.controlMode === ControlModeEnum.VIEW; const zoomLevel = calculateZoomLevel(this.props.flycam, this.props.dataset); - + const extentInVoxel = getDatasetExtentInVoxel(this.props.dataset); + const extent = getDatasetExtentInLength(this.props.dataset); return (
{this.getTracingName(isPublicViewMode)} {this.getDatasetName(isPublicViewMode)} -

Viewport Width: {formatZoomLevel(zoomLevel)}

+

Viewport Width: {formatNumberToLength(zoomLevel)}

Dataset Resolution: {formatScale(this.props.dataset.dataSource.scale)}

+

+ + + + + + + + + + +
Dataset Extent:{formatExtentWithLength(extentInVoxel, x => `${x}`)} Voxel³
+ {formatExtentWithLength(extent, formatNumberToLength)}
+

{this.getTracingStatistics()} {this.getKeyboardShortcuts(isPublicViewMode)} diff --git a/app/assets/javascripts/oxalis/view/right-menu/trees_tab_view.js b/app/assets/javascripts/oxalis/view/right-menu/trees_tab_view.js index 1216c853169..f143835faea 100644 --- a/app/assets/javascripts/oxalis/view/right-menu/trees_tab_view.js +++ b/app/assets/javascripts/oxalis/view/right-menu/trees_tab_view.js @@ -187,25 +187,25 @@ class TreesTabView extends React.PureComponent { getActionsDropdown() { return ( - -
- Change Color -
+ + Change Color - -
- Shuffle All Colors -
+ + Shuffle All Colors - -
- Download Selected Trees -
+ + Download Selected Trees - -
- Import NML -
+ + Import NML
); diff --git a/app/assets/javascripts/oxalis/view/scalebar.js b/app/assets/javascripts/oxalis/view/scalebar.js index d141015e0e9..7cc17c12e22 100644 --- a/app/assets/javascripts/oxalis/view/scalebar.js +++ b/app/assets/javascripts/oxalis/view/scalebar.js @@ -4,7 +4,10 @@ import * as React from "react"; import { connect } from "react-redux"; import constants, { OUTER_BORDER_ORTHO } from "oxalis/constants"; import type { OxalisState, Flycam } from "oxalis/store"; -import { calculateZoomLevel, formatZoomLevel } from "oxalis/view/right-menu/dataset_info_tab_view"; +import { + calculateZoomLevel, + formatNumberToLength, +} from "oxalis/view/right-menu/dataset_info_tab_view"; import type { APIDataset } from "admin/api_flow_types"; type Props = {| @@ -42,7 +45,7 @@ function Scalebar({ flycam, dataset }: Props) { borderRight: "1px solid", }} > - {formatZoomLevel(calculateZoomLevel(flycam, dataset) * scalebarWidthPercentage)} + {formatNumberToLength(calculateZoomLevel(flycam, dataset) * scalebarWidthPercentage)}
); diff --git a/app/assets/javascripts/test/snapshots/public/test-bundle/test/enzyme/snapshot.e2e.js.md b/app/assets/javascripts/test/snapshots/public/test-bundle/test/enzyme/snapshot.e2e.js.md index 896e5394e76..1233840e1eb 100644 --- a/app/assets/javascripts/test/snapshots/public/test-bundle/test/enzyme/snapshot.e2e.js.md +++ b/app/assets/javascripts/test/snapshots/public/test-bundle/test/enzyme/snapshot.e2e.js.md @@ -6060,6 +6060,16 @@ Generated by [AVA](https://ava.li).
␊ ␊ ␊ + ␊ + ␊ ␊ diff --git a/app/assets/javascripts/test/snapshots/public/test-bundle/test/enzyme/snapshot.e2e.js.snap b/app/assets/javascripts/test/snapshots/public/test-bundle/test/enzyme/snapshot.e2e.js.snap index 98e8fe12791..da2f2f01d60 100644 Binary files a/app/assets/javascripts/test/snapshots/public/test-bundle/test/enzyme/snapshot.e2e.js.snap and b/app/assets/javascripts/test/snapshots/public/test-bundle/test/enzyme/snapshot.e2e.js.snap differ diff --git a/app/assets/stylesheets/trace_view/_action_bar.less b/app/assets/stylesheets/trace_view/_action_bar.less index 7c66a815e22..ca33759bab0 100644 --- a/app/assets/stylesheets/trace_view/_action_bar.less +++ b/app/assets/stylesheets/trace_view/_action_bar.less @@ -35,6 +35,10 @@ margin-right: 36px; } +.full-width { + width: 100%; +} + .bullet-point-less-li { list-style-type: none; } diff --git a/app/controllers/AnnotationIOController.scala b/app/controllers/AnnotationIOController.scala index 981d2a2f7b0..fc9afe2aebe 100755 --- a/app/controllers/AnnotationIOController.scala +++ b/app/controllers/AnnotationIOController.scala @@ -118,7 +118,7 @@ class AnnotationIOController @Inject()(nmlWriter: NmlWriter, _ <- bool2Fox(skeletonTracings.nonEmpty || volumeTracingsWithDataLocations.nonEmpty) ?~> "nml.file.noFile" _ <- bool2Fox(volumeTracingsWithDataLocations.size <= 1) ?~> "nml.file.multipleVolumes" dataSetName <- assertAllOnSameDataSet(skeletonTracings, volumeTracingsWithDataLocations.headOption.map(_._1)) ?~> "nml.file.differentDatasets" - dataSet <- dataSetDAO.findOneByNameAndOrganization(dataSetName, request.identity._organization) + dataSet <- dataSetDAO.findOneByNameAndOrganization(dataSetName, request.identity._organization) ?~> "dataSet.noAccess" tracingStoreClient <- tracingStoreService.clientFor(dataSet) volumeTracingIdOpt <- Fox.runOptional(volumeTracingsWithDataLocations.headOption){ v => for { diff --git a/app/controllers/TaskController.scala b/app/controllers/TaskController.scala index 689582d1460..1a21243cadd 100755 --- a/app/controllers/TaskController.scala +++ b/app/controllers/TaskController.scala @@ -94,16 +94,20 @@ class TaskController @Inject() (annotationService: AnnotationService, def create = sil.SecuredAction.async(validateJson[List[TaskParameters]]) { implicit request => - createTasks(request.body.map { params => - val tracing = annotationService.createTracingBase(params.dataSet, params.boundingBox, params.editPosition, params.editRotation) - (params, tracing) - }) + for { + _ <- bool2Fox(request.body.length <= 1000) ?~> "task.create.limitExceeded" + result <- createTasks(request.body.map { params => + val tracing = annotationService.createTracingBase(params.dataSet, params.boundingBox, params.editPosition, params.editRotation) + (params, tracing) + }) + } yield result } def createFromFiles = sil.SecuredAction.async { implicit request => for { body <- request.body.asMultipartFormData ?~> "binary.payload.invalid" inputFiles = body.files.filter(file => file.filename.toLowerCase.endsWith(".nml") || file.filename.toLowerCase.endsWith(".zip")) + _ <- bool2Fox(inputFiles.length <= 1000) ?~> "task.create.limitExceeded" _ <- bool2Fox(inputFiles.nonEmpty) ?~> "nml.file.notFound" jsonString <- body.dataParts.get("formJSON").flatMap(_.headOption) ?~> "format.json.missing" params <- JsonHelper.parseJsonToFox[NmlTaskParameters](jsonString) ?~> "task.create.failed" @@ -263,7 +267,6 @@ class TaskController @Inject() (annotationService: AnnotationService, } def listTasks = sil.SecuredAction.async(parse.json) { implicit request => - for { userIdOpt <- Fox.runOptional((request.body \ "user").asOpt[String])(ObjectId.parse) projectNameOpt = (request.body \ "project").asOpt[String] diff --git a/app/models/annotation/AnnotationService.scala b/app/models/annotation/AnnotationService.scala index 8ec07f79fb5..140f338ae29 100755 --- a/app/models/annotation/AnnotationService.scala +++ b/app/models/annotation/AnnotationService.scala @@ -220,7 +220,7 @@ class AnnotationService @Inject()(annotationInformationProvider: AnnotationInfor def useAsTemplateAndInsert(annotation: Annotation) = { for { dataSetName <- dataSetDAO.getNameById(annotation._dataSet)(GlobalAccessContext) ?~> "dataSet.notFound" - dataSet <- dataSetDAO.findOne(annotation._dataSet) ?~> ("Could not access DataSet " + dataSetName + ". Does your team have access?") + dataSet <- dataSetDAO.findOne(annotation._dataSet) ?~> "dataSet.noAccess" newTracingId <- tracingFromBase(annotation, dataSet) ?~> "Failed to use annotation base as template." newAnnotation = annotation.copy( _id = initializingAnnotationId, diff --git a/app/models/annotation/TracingStoreRpcClient.scala b/app/models/annotation/TracingStoreRpcClient.scala index afa67050685..ed68cd4392c 100644 --- a/app/models/annotation/TracingStoreRpcClient.scala +++ b/app/models/annotation/TracingStoreRpcClient.scala @@ -109,7 +109,7 @@ class TracingStoreRpcClient(tracingStore: TracingStore, dataSet: DataSet, rpc: R tracing <- rpc(s"${tracingStore.url}/tracings/volume/${tracingId}") .addQueryString("token" -> TracingStoreRpcClient.webKnossosToken) .getWithProtoResponse[VolumeTracing](VolumeTracing) - data <- rpc(s"${tracingStore.url}/tracings/volume/${tracingId}/getAllData") + data <- rpc(s"${tracingStore.url}/tracings/volume/${tracingId}/allData") .addQueryString("token" -> TracingStoreRpcClient.webKnossosToken) .getStream } yield { diff --git a/conf/messages b/conf/messages index 6ebb5b61879..e4313c877df 100644 --- a/conf/messages +++ b/conf/messages @@ -77,6 +77,7 @@ braintracing.exists=Great, you already have an account on braintracing.org. Plea dataSet=Dataset dataSet.file=Dataset ZIP File dataSet.notFound=Dataset {0} doesn’t exist +dataSet.noAccess=Could not access DataSet {0}. Does your team have access? dataSet.notImported=Dataset {0} is not imported dataSet.name.invalid=A dataset name can only contain letters, digits and underscores dataSet.import.impossible.name=Import impossible. Dataset name can only consist of a-Z, 0-9, "-" and "_". @@ -187,6 +188,7 @@ annotation.makeHybrid.alreadyHybrid=Could not convert annotation to hybrid annot task.create.success=Task successfully created task.create.failed=Failed to create Task +task.create.limitExceeded=Cannot create more than 1000 tasks in one request. task.finished=Task is finished task.assigned=You got a new task task.tooManyOpenOnes=You already have too many open tasks