Skip to content

Commit

Permalink
Multi-Resolution Volume Tracings (#4755)
Browse files Browse the repository at this point in the history
* keep fixed zoom during td rotation to planes

* refactor 3d rotation method to keep the zoom and simplify it

* fix position and up vector not getting interpolated correctly

* add changelog entry

* Apply suggestions from code review

Co-authored-by: Daniel <[email protected]>

* remove zReductionFactor

* backend: support anisotropic resolutions in volume bucket keys

* fix 3d rotation preset

* remove useless check for valid values

* remove reassignment of width and height

* do volume annotation in all resolutions

* Update frontend/javascripts/oxalis/controller/camera_controller.js

Co-authored-by: Philipp Otto <[email protected]>

* made code pretty

* reduce max brush size

* disallow trace tool in higher resolutions

* enable brushing in high again

* do not block resolution loading

* annotate z many slices at once when z resolution is > 1

* fixing a few tests

* hopefully fixed all tests

* Add info about annotating multiple slices

* limit area that gets autofilled

* [WIP] lazy downscaling of volume buckets

* lookup-based live downsampling

* [WIP] downsample volume tracings on upload

* [WIP] downscale during upload

* fix offsets, switch to mode

* determine which mags to downsample

* adapt nml unit test suite

* fix backend dummy tracing

* move function to get number of slices to accessor

* WIP: applying flood fill to all resolutions

* [WIP] track mags in volumetracing object

* fix calculating error during downsampling

* fix flood fill for all source resolutions

* fixed flood fill for all resolutions

* handle differing resolutions during volume merging + upload

* add up and downsampling of LabeledVoxelsMap

* add unlinkFallback route

* changelog

* cleanup backend code

* fixed up and downsampling and added tests

* added method to apply a voxel map

* fixed upsampling and added regression test

* add comments to sampling methods

* make copylayer work in multi resolutions

* while upsampling annotate mutliple slices

* Add todo about refactoring labeling voxels

* adjusted voxel labeling method to label in all resolutions the segmentation layer has

* added some comments with todos

* Update frontend/javascripts/oxalis/model/accessors/volumetracing_accessor.js

* always label in all resolutions when using api; show stack-icon when multi-slice-annotating

* more comments

* replace old unlinkFallbackLayer with new API for that (keep old UpdateAction for backwards compatibility)

* fix wrong parameter format

* refactor resolution handling to deal with sparse resolutions better (WIP/1)

* undo temporary new_resolutions change

* continue refactoring resolution handling to deal with sparse resolutions better (WIP/2) --> brush, paint-bucket and copy-segmentation tool seem to work

* fix picking segment color when being in a not-existing segmentation mag

* ensure that prefetch saga, bucket picker and pull queue don't try to load buckets for not existing mags

* ensure legacy getResolutions method provides dense resolution set; clean up

* fix initialization and volume annotation sampling spec

* fix CI

* update snapshots

* tune warning text

* make merger mode work in higher resolutions

* show a status indicator in the viewports if a layer cannot be rendered due to the zoom step

* reduce tooltip rerenderings by making styles constant

* fix enabled 'Render Missing Data Black' for missing mags

* add issue links to todo comments

* implement some feedback

* Apply suggestions from code review

Co-authored-by: Daniel <[email protected]>

* more PR feedback

* also implement getIndexOrClosestHigherIndex for ResolutionInfo and use where appropriate

* return true in isVolumeTraceToolDisallowed if no volume tracing exists

* fix front-end merge conflicts

* reduce usages of getResolutionByIndexWithFallback and make that function correct (remove isotropic fallback in favor of failing for unclear cases

* simplify is2DVoxelInsideBucket method in Bucket

* Apply suggestions from code review

Co-authored-by: Daniel <[email protected]>

* integrate more PR feedback; resolve merge conflict in changelog; rename missing to unrenderable etc

* refactor applyLabeledVoxelMapToAllMissingResolutions

* further refactoring of applyLabeledVoxelMapToAllMissingResolutions

* remove unused import

* fix flow after upgrade

* improve comment for LabeledVoxelsMap

* fix uniform definion

* fix getDataValue for missing resolution

* Improve performance of volume annotation tools (#4848)

* tmp: use LabeledVoxelsMap for brushing and TypedArray for brushing map (first measurements yield 20x better performance)

* restore all of the brush functionality; create LabeledVoxelMap in current mag; further performance optimizations

* clean up VoxelIterator a bit

* more clean up

* clean up and fix CI

* further clean up

* remove todo

* use unzoomed centroid when updating direction

* fix contour drawing in mag > 1

* fix flow

* fix circle-drawing for anisotropic mags

* update changelog

* rename VoxelIterator to VoxelBuffer2D

* optimize draw circle method by comparing squared dist with squared radius

* rename variables and change comment for bx and by

* remove obsolete comment

* add another comment to fillCircle call

* ensure that rendered volume magnification is also the mag in which is
annotated

- also ensure that the volume toolbar is disabled properly when the
segmentation cannot be rendered for some reason

* Update frontend/javascripts/oxalis/model/sagas/volumetracing_saga.js

Co-authored-by: Daniel <[email protected]>

* fix issues from merging master

* fix upload/download

* fix issues after merge

* disable continuous drawing for now since its performance needs to be adapted to the multi-resolution feature

* fix linting

* adapt headline in import modal

* change tracing to annotation

* change magnification to resolution in user-facing strings and some internal ones

* make resolution-warning less verbose

* try to fix e.trigger is not a function

* fix linting

* fix bug in applyLabeledVoxelMap

* fix flow

* show fallback-not-included warning only when there is fallback data

* fix anisotropic bucket adresses

* treat empty resolution list as no resolution list

* change Boolean() to != null

* fix flow in model initialization

* ensure volume layer is saved before reloading; fixes #4857

* format

* disable copy-segmentation feature for higher mags due to performance

* fix some merge-related problems

* fix styling of multi-slice-icon

* throw an exception if the resolution could not be looked up instead of defaulting to 0,0,0

* look up volume bucket resolution by zoom step ** 2 rather than index

* fix that toolbar rerendered on every state change

* improve handling of volume-is-disabled case in toolbar

* replace exception by fox.failure if volume resolution lookup fails

* show data type and resolutions of layer in tooltip next to layer name (therefore, the dtype tag is removed)

* fix syntax error

* Enforce TaskType Resolution Restrictions for Multi-Res Volume Tasks (#4891)

* [WIP] enforce task type magnification settings in tasks

* pass allowedMagnifications to rpc methods

* pass magnifications as query string

* respect resolution restrictions for volume tracings.

* add option to downsample existing volume tracing

* remove outdated assertion, update resolution list after downsampling

* sleep 10s

* remove sleep and bump dev-proxy-timeout to 5 min

* fix task creation with resolution restrictions

* use inclusive check

* when uploading zips with differing resolution sets, fail

* remove unused import

* refresh snapshots (tracings contain organization name)

* write header.wkw to every mag in volume download, fix anisotropic directory names

* add resolution-samenss assertion in volume dnd import. fix flow types

* fix restriction passing in initial data

* Add button to trigger downsampling of volume annotation.

The reload button next to the active-resolution-indicator only appears
for explorative annotations with volume data. It opens a modal which
explains the action and its consequences. While the operation is
running, the modal blocks further usages of webKnossos. After the
operation has completed, the complete page is reloaded.

* fix parameter passing in duplicate rpc

* avoid undefined behavior of bucket iterator by checking hasNext. do not relabel when “merging” only one volume

* make tasktype resolution restrictions immutable. sort resolution list

* update migration guide

* remove debug output

* improve initiate-volume-downsampling modal and move its button to the layer settings

* show toast when mag-restriction is violated instead of prohibiting the zoom change in the first place

* don't allow editing mag-restrictions in already created tasktype

* remove some more debug output, unify variable names

* forbid creating new nodes or using volume tools when the current mag was forbidden by the task type

* clean up

* Update MIGRATIONS.unreleased.md

Co-authored-by: Philipp Otto <[email protected]>

* fix styling of button-link

* use ListBuffer instead of HashSet as per pr feedback

* integrate some feedback

* Apply suggestions from code review

Co-authored-by: Daniel <[email protected]>

* remove unnecessary SET_TOOL dependence

* do map ids if initialLargestSegmentId != 0

Co-authored-by: Philipp Otto <[email protected]>
Co-authored-by: Philipp Otto <[email protected]>
Co-authored-by: Daniel <[email protected]>

Co-authored-by: Michael Büßemeyer <[email protected]>
Co-authored-by: MichaelBuessemeyer <[email protected]>
Co-authored-by: Daniel <[email protected]>
Co-authored-by: Philipp Otto <[email protected]>
Co-authored-by: Youri K <[email protected]>
Co-authored-by: Philipp Otto <[email protected]>
  • Loading branch information
7 people authored Nov 9, 2020
1 parent 96b14eb commit ecaffb0
Show file tree
Hide file tree
Showing 105 changed files with 3,946 additions and 939 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
- The total length of skeletons can now be measured using the dropdown in the tree list tab. Also, the frontend API received the methods `api.tracing.measureTreeLength` and `api.tracing.measureAllTrees`. [#4898](https://github.com/scalableminds/webknossos/pull/4898)
- Introduced an indeterminate visibility state for groups in the tree tab if not all but only some of the group's children are visible. Before, the visibility of those groups was shown as not visible which made it hard to find the visible trees. [#4897](https://github.com/scalableminds/webknossos/pull/4897)
- Dataset uploads on a specific Datastore can now be restricted to a single organization. [#4892](https://github.com/scalableminds/webknossos/pull/4892)
- Added multi-resolution volume annotations. Note that already existing volume tracings will still only contain data in the first magnification. If you want to migrate an old volume tracing, you can download and re-import it. [#4755](https://github.com/scalableminds/webknossos/pull/4755)

### Changed
- In the tree tab, all groups but the root group are now collapsed instead of expanded when opening a tracing. [#4897](https://github.com/scalableminds/webknossos/pull/4897)
- New volume/hybrid annotations are now automatically multi-resolution volume annotations. [#4755](https://github.com/scalableminds/webknossos/pull/4755)
- Improved performance of volume annotations (brush and trace tool). [#4848](https://github.com/scalableminds/webknossos/pull/4848)

### Fixed
- Fixed the disappearing of dataset settings after switching between view mode and annotation mode. [#4845](https://github.com/scalableminds/webknossos/pull/4845)
Expand Down
8 changes: 7 additions & 1 deletion MIGRATIONS.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ This project adheres to [Calendar Versioning](http://calver.org/) `0Y.0M.MICRO`.
User-facing changes are documented in the [changelog](CHANGELOG.released.md).

## Unreleased
-
- As volume annotations in arbitrary magnifications are now supported and the behavior of magnification restrictions of tasks has changed (allow full zoom, but disable tools unless in correct magnification), you may want to restrict all volume and hybrid task types to mag 1 to achieve the old behavior (mag1-only):
```
update webknossos.tasktypes
set settings_allowedmagnifications = '{"min":1,"max":1,"shouldRestrict":true}'
where (tracingtype = 'volume' or tracingtype = 'hybrid')
and (settings_allowedmagnifications is null or settings_allowedmagnifications::json->>'shouldRestrict'='false');
```

### Postgres Evolutions:
- [057-add-layer-specific-view-configs.sql](conf/evolutions/056-add-layer-specific-view-configs.sql)
Expand Down
31 changes: 30 additions & 1 deletion app/controllers/AnnotationController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -166,14 +166,43 @@ class AnnotationController @Inject()(
for {
_ <- bool2Fox(AnnotationType.Explorational.toString == typ) ?~> "annotation.makeHybrid.explorationalsOnly"
annotation <- provider.provideAnnotation(typ, id, request.identity)
_ <- annotationService.makeAnnotationHybrid(annotation) ?~> "annotation.makeHybrid.failed"
organization <- organizationDAO.findOne(request.identity._organization)
_ <- annotationService.makeAnnotationHybrid(annotation, organization.name) ?~> "annotation.makeHybrid.failed"
updated <- provider.provideAnnotation(typ, id, request.identity)
json <- annotationService.publicWrites(updated, Some(request.identity)) ?~> "annotation.write.failed"
} yield {
JsonOk(json)
}
}

def downsample(typ: String, id: String) = sil.SecuredAction.async { implicit request =>
for {
_ <- bool2Fox(AnnotationType.Explorational.toString == typ) ?~> "annotation.downsample.explorationalsOnly"
annotation <- provider.provideAnnotation(typ, id, request.identity)
_ <- annotationService.downsampleAnnotation(annotation) ?~> "annotation.downsample.failed"
updated <- provider.provideAnnotation(typ, id, request.identity)
json <- annotationService.publicWrites(updated, Some(request.identity)) ?~> "annotation.write.failed"
} yield {
JsonOk(json)
}
}

def unlinkFallback(typ: String, id: String) = sil.SecuredAction.async { implicit request =>
for {
_ <- bool2Fox(AnnotationType.Explorational.toString == typ) ?~> "annotation.unlinkFallback.explorationalsOnly"
annotation <- provider.provideAnnotation(typ, id, request.identity)
volumeTracingId <- annotation.volumeTracingId.toFox ?~> "annotation.unlinkFallback.noVolume"
dataSet <- dataSetDAO
.findOne(annotation._dataSet)(GlobalAccessContext) ?~> "dataSet.notFoundForAnnotation" ~> NOT_FOUND
dataSource <- dataSetService.dataSourceFor(dataSet).flatMap(_.toUsable) ?~> "dataSet.notImported"
tracingStoreClient <- tracingStoreService.clientFor(dataSet)
newTracingId <- tracingStoreClient.unlinkFallback(volumeTracingId, dataSource)
_ <- annotationDAO.updateVolumeTracingId(annotation._id, newTracingId)
updatedAnnotation <- provider.provideAnnotation(typ, id, request.identity)
js <- annotationService.publicWrites(updatedAnnotation, Some(request.identity))
} yield JsonOk(js)
}

private def finishAnnotation(typ: String, id: String, issuingUser: User, timestamp: Long)(
implicit ctx: DBAccessContext): Fox[(Annotation, String)] =
for {
Expand Down
27 changes: 13 additions & 14 deletions app/controllers/AnnotationIOController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,7 @@ class AnnotationIOController @Inject()(nmlWriter: NmlWriter,

for {
_ <- bool2Fox(skeletonTracings.nonEmpty || volumeTracingsWithDataLocations.nonEmpty) ?~> "nml.file.noFile"
dataSet <- findDataSetForUploadedAnnotations(skeletonTracings,
volumeTracingsWithDataLocations.map(_._1),
parseSuccesses)
dataSet <- findDataSetForUploadedAnnotations(skeletonTracings, volumeTracingsWithDataLocations.map(_._1))
tracingStoreClient <- tracingStoreService.clientFor(dataSet)
mergedVolumeTracingIdOpt <- Fox.runOptional(volumeTracingsWithDataLocations.headOption) { _ =>
for {
Expand Down Expand Up @@ -120,11 +118,10 @@ class AnnotationIOController @Inject()(nmlWriter: NmlWriter,

private def findDataSetForUploadedAnnotations(
skeletonTracings: List[SkeletonTracing],
volumeTracings: List[VolumeTracing],
parseSuccesses: List[NmlParseResult])(implicit mp: MessagesProvider, ctx: DBAccessContext): Fox[DataSet] =
volumeTracings: List[VolumeTracing])(implicit mp: MessagesProvider, ctx: DBAccessContext): Fox[DataSet] =
for {
dataSetName <- assertAllOnSameDataSet(skeletonTracings, volumeTracings) ?~> "nml.file.differentDatasets"
organizationNameOpt <- assertAllOnSameOrganization(parseSuccesses.flatMap(s => s.organizationName)) ?~> "nml.file.differentDatasets"
organizationNameOpt <- assertAllOnSameOrganization(skeletonTracings, volumeTracings) ?~> "nml.file.differentDatasets"
organizationIdOpt <- Fox.runOptional(organizationNameOpt) {
organizationDAO.findOneByName(_)(GlobalAccessContext).map(_._id)
} ?~> Messages("organization.notFound", organizationNameOpt.getOrElse("")) ~> NOT_FOUND
Expand Down Expand Up @@ -172,14 +169,16 @@ class AnnotationIOController @Inject()(nmlWriter: NmlWriter,
_ <- bool2Fox(volumes.forall(_.dataSetName == dataSetName))
} yield dataSetName

private def assertAllOnSameOrganization(organizationNames: List[String]): Fox[Option[String]] =
if (organizationNames.isEmpty) Fox.successful(None)
else {
for {
organizationName <- organizationNames.headOption.toFox
_ <- bool2Fox(organizationNames.forall(name => name == organizationName))
} yield Some(organizationName)
}
private def assertAllOnSameOrganization(skeletons: List[SkeletonTracing],
volumes: List[VolumeTracing]): Fox[Option[String]] =
for {
organizationName: Option[String] <- volumes.headOption
.map(_.organizationName)
.orElse(skeletons.headOption.map(_.organizationName))
.toFox
_ <- bool2Fox(skeletons.forall(_.organizationName == organizationName))
_ <- bool2Fox(volumes.forall(_.organizationName == organizationName))
} yield organizationName

private def adaptPropertiesToFallbackLayer(volumeTracing: VolumeTracing, dataSet: DataSet)(
implicit ctx: DBAccessContext): Fox[VolumeTracing] =
Expand Down
75 changes: 49 additions & 26 deletions app/controllers/TaskController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import com.scalableminds.util.geometry.{BoundingBox, Point3D, Vector3D}
import com.scalableminds.util.mvc.ResultBox
import com.scalableminds.util.tools.{Fox, FoxImplicits, JsonHelper}
import com.scalableminds.webknossos.tracingstore.SkeletonTracing.{SkeletonTracing, SkeletonTracingOpt, SkeletonTracings}
import com.scalableminds.webknossos.tracingstore.VolumeTracing.{VolumeTracing, VolumeTracingOpt, VolumeTracings}
import com.scalableminds.webknossos.tracingstore.VolumeTracing.VolumeTracing
import com.scalableminds.webknossos.tracingstore.tracings.volume.ResolutionRestrictions
import com.scalableminds.webknossos.tracingstore.tracings.{ProtoGeometryImplicits, TracingType}
import javax.inject.Inject
import models.annotation.nml.NmlResults.NmlParseResult
import models.annotation.nml.NmlService
import models.annotation._
import models.binary.{DataSet, DataSetDAO, DataSetService}
import models.binary.{DataSet, DataSetDAO}
import models.project.{Project, ProjectDAO}
import models.task._
import models.team.{Team, TeamDAO}
Expand Down Expand Up @@ -74,7 +74,6 @@ class TaskController @Inject()(annotationDAO: AnnotationDAO,
dataSetDAO: DataSetDAO,
userTeamRolesDAO: UserTeamRolesDAO,
userService: UserService,
dataSetService: DataSetService,
tracingStoreService: TracingStoreService,
teamDAO: TeamDAO,
taskDAO: TaskDAO,
Expand All @@ -88,7 +87,7 @@ class TaskController @Inject()(annotationDAO: AnnotationDAO,
with ProtoGeometryImplicits
with FoxImplicits {

val MAX_OPEN_TASKS = conf.WebKnossos.Tasks.maxOpenPerUser
private val MAX_OPEN_TASKS: Int = conf.WebKnossos.Tasks.maxOpenPerUser

def read(taskId: String) = sil.SecuredAction.async { implicit request =>
for {
Expand All @@ -114,8 +113,9 @@ class TaskController @Inject()(annotationDAO: AnnotationDAO,
} yield result
}

def duplicateAllBaseTracings(taskParametersList: List[TaskParameters],
organizationId: ObjectId)(implicit ctx: DBAccessContext, m: MessagesProvider) =
private def duplicateAllBaseTracings(taskParametersList: List[TaskParameters], organizationId: ObjectId)(
implicit ctx: DBAccessContext,
m: MessagesProvider): Fox[List[TaskParameters]] =
Fox.serialCombined(taskParametersList)(
params =>
Fox
Expand All @@ -141,9 +141,10 @@ class TaskController @Inject()(annotationDAO: AnnotationDAO,
annotation: Annotation,
params: TaskParameters,
tracingStoreClient: TracingStoreRpcClient,
organizationId: ObjectId)(implicit ctx: DBAccessContext, m: MessagesProvider): Fox[String] =
organizationId: ObjectId,
resolutionRestrictions: ResolutionRestrictions)(implicit ctx: DBAccessContext, m: MessagesProvider): Fox[String] =
annotation.volumeTracingId
.map(id => tracingStoreClient.duplicateVolumeTracing(id))
.map(id => tracingStoreClient.duplicateVolumeTracing(id, resolutionRestrictions = resolutionRestrictions))
.getOrElse(
annotationService
.createVolumeTracingBase(
Expand All @@ -152,13 +153,15 @@ class TaskController @Inject()(annotationDAO: AnnotationDAO,
params.boundingBox,
params.editPosition,
params.editRotation,
false
volumeShowFallbackLayer = false,
resolutionRestrictions = resolutionRestrictions
)
.flatMap(tracingStoreClient.saveVolumeTracing(_)))
.flatMap(tracingStoreClient.saveVolumeTracing(_, resolutionRestrictions = resolutionRestrictions)))

def duplicateBaseTracings(baseAnnotation: BaseAnnotation, taskParameters: TaskParameters, organizationId: ObjectId)(
implicit ctx: DBAccessContext,
m: MessagesProvider) = {
private def duplicateBaseTracings(
baseAnnotation: BaseAnnotation,
taskParameters: TaskParameters,
organizationId: ObjectId)(implicit ctx: DBAccessContext, m: MessagesProvider): Fox[BaseAnnotation] = {

@SuppressWarnings(Array("TraversableHead")) // We check if nonCancelledTaskAnnotations are empty before so head always works
def checkForTask(taskId: ObjectId): Fox[Annotation] =
Expand Down Expand Up @@ -196,13 +199,16 @@ class TaskController @Inject()(annotationDAO: AnnotationDAO,
duplicateSkeletonTracingOrCreateSkeletonTracingBase(annotation, taskParameters, tracingStoreClient).map(Some(_))
else Fox.successful(None)
newVolumeId <- if (taskType.tracingType == TracingType.volume || taskType.tracingType == TracingType.hybrid)
duplicateVolumeTracingOrCreateVolumeTracingBase(annotation, taskParameters, tracingStoreClient, organizationId)
.map(Some(_))
duplicateVolumeTracingOrCreateVolumeTracingBase(annotation,
taskParameters,
tracingStoreClient,
organizationId,
taskType.settings.resolutionRestrictions).map(Some(_))
else Fox.successful(None)
} yield BaseAnnotation(baseAnnotationIdValidated.id, newSkeletonId, newVolumeId)
}

def createTaskSkeletonTracingBases(paramsList: List[TaskParameters])(
private def createTaskSkeletonTracingBases(paramsList: List[TaskParameters])(
implicit ctx: DBAccessContext,
m: MessagesProvider): Fox[List[Option[SkeletonTracing]]] =
Fox.serialCombined(paramsList) { params =>
Expand All @@ -221,7 +227,7 @@ class TaskController @Inject()(annotationDAO: AnnotationDAO,
} yield skeletonTracingOpt
}

def createTaskVolumeTracingBases(paramsList: List[TaskParameters], organizationId: ObjectId)(
private def createTaskVolumeTracingBases(paramsList: List[TaskParameters], organizationId: ObjectId)(
implicit ctx: DBAccessContext,
m: MessagesProvider): Fox[List[Option[(VolumeTracing, Option[File])]]] =
Fox.serialCombined(paramsList) { params =>
Expand All @@ -236,7 +242,8 @@ class TaskController @Inject()(annotationDAO: AnnotationDAO,
params.boundingBox,
params.editPosition,
params.editRotation,
volumeShowFallbackLayer = false
volumeShowFallbackLayer = false,
resolutionRestrictions = taskType.settings.resolutionRestrictions
)
.map(v => Some((v, None)))
} else Fox.successful(None)
Expand Down Expand Up @@ -296,7 +303,8 @@ class TaskController @Inject()(annotationDAO: AnnotationDAO,
params.boundingBox,
params.editPosition,
params.editRotation,
volumeShowFallbackLayer = false
volumeShowFallbackLayer = false,
resolutionRestrictions = taskType.settings.resolutionRestrictions
)
.map(v => (v, None)))

Expand Down Expand Up @@ -449,6 +457,7 @@ class TaskController @Inject()(annotationDAO: AnnotationDAO,
bool2Fox(fullTasks.forall(tuple => tuple._1.baseAnnotation.isDefined || tuple._2.isDefined || tuple._3.isDefined))

def assertAllOnSameDataset(firstDatasetName: String): Fox[String] = {
@scala.annotation.tailrec
def allOnSameDatasetIter(
requestedTasksRest: List[(TaskParameters, Option[SkeletonTracing], Option[(VolumeTracing, Option[File])])],
dataSetName: String): Boolean =
Expand Down Expand Up @@ -490,12 +499,8 @@ class TaskController @Inject()(annotationDAO: AnnotationDAO,
case _ => savedId
}
}
volumeTracingIds: List[Box[Option[String]]] <- Fox.sequence(requestedTasks.map(_.map(_._3)).map {
case Full(Some((tracing, initialFile))) =>
tracingStoreClient.saveVolumeTracing(tracing, initialFile).map(Some(_))
case f: Failure => box2Fox(f)
case _ => Fox.successful(None)
})
volumeTracingIds: List[Box[Option[String]]] <- Fox.sequence(
requestedTasks.map(requestedTask => saveVolumeTracingIfPresent(requestedTask, tracingStoreClient)))
skeletonTracingsIdsMerged = mergeTracingIds((requestedTasks.map(_.map(_._1)), skeletonTracingIds).zipped.toList,
isSkeletonId = true)
volumeTracingsIdsMerged = mergeTracingIds((requestedTasks.map(_.map(_._1)), volumeTracingIds).zipped.toList,
Expand Down Expand Up @@ -526,6 +531,24 @@ class TaskController @Inject()(annotationDAO: AnnotationDAO,
} yield Ok(Json.toJson(result))
}

private def saveVolumeTracingIfPresent(
requestedTaskBox: Box[(TaskParameters, Option[SkeletonTracing], Option[(VolumeTracing, Option[File])])],
tracingStoreClient: TracingStoreRpcClient)(implicit ctx: DBAccessContext): Fox[Option[String]] =
requestedTaskBox.map { tuple =>
(tuple._1, tuple._3)
} match {
case Full((params: TaskParameters, Some((tracing, initialFile)))) =>
for {
taskTypeIdValidated <- ObjectId.parse(params.taskTypeId) ?~> "taskType.id.invalid"
taskType <- taskTypeDAO.findOne(taskTypeIdValidated) ?~> "taskType.notFound"
saveResult <- tracingStoreClient
.saveVolumeTracing(tracing, initialFile, resolutionRestrictions = taskType.settings.resolutionRestrictions)
.map(Some(_))
} yield saveResult
case f: Failure => box2Fox(f)
case _ => Fox.successful(None)
}

private def warnIfTeamHasNoAccess(requestedTasks: List[TaskParameters], dataSet: DataSet)(
implicit ctx: DBAccessContext): Fox[List[String]] = {
val projectNames = requestedTasks.map(_.projectName).distinct
Expand Down
1 change: 1 addition & 0 deletions app/controllers/TaskTypeController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class TaskTypeController @Inject()(taskTypeDAO: TaskTypeDAO,
taskTypeIdValidated <- ObjectId.parse(taskTypeId) ?~> "taskType.id.invalid"
taskType <- taskTypeDAO.findOne(taskTypeIdValidated) ?~> "taskType.notFound" ~> NOT_FOUND
_ <- bool2Fox(taskTypeFromForm.tracingType == taskType.tracingType) ?~> "taskType.tracingTypeImmutable"
_ <- bool2Fox(taskTypeFromForm.settings.allowedMagnifications == taskType.settings.allowedMagnifications) ?~> "taskType.allowedMagnificationsImmutable"
updatedTaskType = taskTypeFromForm.copy(_id = taskType._id)
_ <- Fox.assertTrue(userService.isTeamManagerOrAdminOf(request.identity, taskType._team)) ?~> "notAllowed" ~> FORBIDDEN
_ <- Fox
Expand Down
1 change: 1 addition & 0 deletions app/controllers/TracingStoreController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,5 @@ class TracingStoreController @Inject()(tracingStoreService: TracingStoreService,
} yield { Ok(Json.toJson(js)) }
}
}

}
Loading

0 comments on commit ecaffb0

Please sign in to comment.