-
Notifications
You must be signed in to change notification settings - Fork 29
Virtual Datasets #8708
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
Open
frcroth
wants to merge
60
commits into
master
Choose a base branch
from
explore-virtual-datasets
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Virtual Datasets #8708
Changes from 41 commits
Commits
Show all changes
60 commits
Select commit
Hold shift + click to select a range
9f89d38
Explore remote datasets as virtual datasets
frcroth 42101a9
Do not have virtual remote datasets deleted
frcroth 18dfe98
Put mag in db
frcroth 9c3cf74
Add temporary front end for testing virtual datasets
frcroth 391227a
Use mags for WKW datasets
frcroth 916542d
Merge branch 'master' into explore-virtual-datasets
frcroth 3b3b13c
Move zarr streaming stuff to service, todo: add controller with datas…
frcroth 3f81a85
Move old zarr routes to LegacyController, update zarr routes to use id
frcroth ac0f66d
Use datasetId in BinaryDataController
frcroth d51dea9
Agglomerate files by dataset id
frcroth 371f3fb
Merge branch 'master' into explore-virtual-datasets
frcroth 611e552
Update more routes to use dataset id
frcroth a4aaff4
Disable deletion route on virtual datasets for now
frcroth 677c8fe
Merge branch 'master' into explore-virtual-datasets
frcroth 5b220ac
Use datasetId for connectome routes
frcroth 0fc1834
Move compose to webknossos
frcroth 6e27ba5
Merge branch 'master' into explore-virtual-datasets
frcroth b1797fc
Fix WKW dataset mags being lost in parsing
frcroth 72de557
Merge branch 'master' into explore-virtual-datasets
frcroth ffdb99f
Adapt RemoteFallbackLayer to use datasetIds
frcroth f4c2c0c
Add 'isVirtual' column to datasets
frcroth f4ec53f
Remove usages of datasource id in rest api
frcroth 3e84232
Handle dataset deletion for virtual dataset
frcroth 82d4796
Update view in migration
frcroth a5edc1c
Merge branch 'master' into explore-virtual-datasets
frcroth 38c85fd
Fix frontend types
frcroth 6c84acd
Try to fix frontend tests
frcroth 50c8839
Lint frontend
frcroth e639ca2
Remove toAbstractLayer
frcroth 85745a2
Make WKW layers datalayerwithmaglocators
frcroth f2a22ac
Use mags in frontend WKW layer type validation
frcroth 8872a46
Fix update data source route for virtual datasets
frcroth 00b336b
Update changelog
frcroth 61bf9d7
Merge branch 'master' into explore-virtual-datasets
frcroth 3f73688
Fix types
frcroth 59b566c
Update docs to use mags for WKW datasets
frcroth 66b035a
Format backend
frcroth 0e64a46
Fix backend lint
frcroth 4bef4a8
Merge branch 'master' into explore-virtual-datasets
frcroth f4cc156
Apply suggestions from the rabbit
frcroth 5f7cf33
refactor frontend a little
MichaelBuessemeyer 7400a89
Merge branch 'master' into explore-virtual-datasets
fm3 8026ddf
adapt schema version number after merge
fm3 67278af
also in migration guide
fm3 91ea116
Use ObjectIds more consistently
frcroth d768d72
Rename legacy controller methods and handle all supported versions
frcroth 04e070b
Remove wkw resolutions from WKWLayer
frcroth a415ecb
Reloading dataset rereads JSON and sends it to WK
frcroth f556fe3
Merge branch 'master' into explore-virtual-datasets
frcroth b8872f0
Invalidate cache on refreshing datasource
frcroth a86c28f
Fix parameters
frcroth 60a7631
Remove length of underlying cubes
frcroth 612ea36
Check for added paths and dont update virtual datasets from datastore
frcroth 952b593
Remove ZarrStreamingService
frcroth 34098c6
Cache datasetid lookups in data store
frcroth 71730e6
Fix uploading annotations
frcroth e33d8b6
Fix legacy controller not being used, add reload legacy route
frcroth 8950842
Allow new paths when updaing dataset by scanning from disk
frcroth 844a563
Fix backend formatting
frcroth 61fb838
Merge branch 'master' into explore-virtual-datasets
frcroth File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,9 +7,9 @@ import com.scalableminds.util.tools.Fox | |
import com.scalableminds.webknossos.datastore.controllers.JobExportProperties | ||
import com.scalableminds.webknossos.datastore.helpers.{LayerMagLinkInfo, MagLinkInfo} | ||
import com.scalableminds.webknossos.datastore.models.UnfinishedUpload | ||
import com.scalableminds.webknossos.datastore.models.datasource.DataSourceId | ||
import com.scalableminds.webknossos.datastore.models.datasource.{AbstractDataLayer, DataSource, DataSourceId} | ||
import com.scalableminds.webknossos.datastore.models.datasource.inbox.{InboxDataSourceLike => InboxDataSource} | ||
import com.scalableminds.webknossos.datastore.services.{DataSourcePathInfo, DataStoreStatus} | ||
import com.scalableminds.webknossos.datastore.services.{DataSourcePathInfo, DataSourceRegistrationInfo, DataStoreStatus} | ||
import com.scalableminds.webknossos.datastore.services.uploading.{ | ||
LinkedLayerIdentifier, | ||
ReserveAdditionalInformation, | ||
|
@@ -242,13 +242,23 @@ class WKRemoteDataStoreController @Inject()( | |
} | ||
} | ||
|
||
def getPaths(name: String, key: String, organizationId: String, directoryName: String): Action[AnyContent] = | ||
def deleteVirtualDataset(name: String, key: String): Action[String] = | ||
Action.async(validateJson[String]) { implicit request => | ||
dataStoreService.validateAccess(name, key) { _ => | ||
for { | ||
datasetIdValidated <- ObjectId.fromString(request.body) ?~> "dataset.delete.invalidId" ~> BAD_REQUEST | ||
dataset <- datasetDAO.findOne(datasetIdValidated)(GlobalAccessContext) ~> NOT_FOUND | ||
_ <- Fox.fromBool(dataset.isVirtual) ?~> "dataset.delete.notVirtual" ~> FORBIDDEN | ||
_ <- datasetDAO.deleteDataset(dataset._id, onlyMarkAsDeleted = true) | ||
} yield Ok | ||
} | ||
} | ||
|
||
def getPaths(name: String, key: String, datasetId: ObjectId): Action[AnyContent] = | ||
Action.async { implicit request => | ||
dataStoreService.validateAccess(name, key) { _ => | ||
for { | ||
organization <- organizationDAO.findOne(organizationId)(GlobalAccessContext) | ||
dataset <- datasetDAO.findOneByDirectoryNameAndOrganization(directoryName, organization._id)( | ||
GlobalAccessContext) | ||
dataset <- datasetDAO.findOne(datasetId)(GlobalAccessContext) ~> NOT_FOUND | ||
layers <- datasetLayerDAO.findAllForDataset(dataset._id) | ||
magsAndLinkedMags <- Fox.serialCombined(layers)(l => datasetService.getPathsForDataLayer(dataset._id, l.name)) | ||
magLinkInfos = magsAndLinkedMags.map(_.map { case (mag, linkedMags) => MagLinkInfo(mag, linkedMags) }) | ||
|
@@ -270,6 +280,49 @@ class WKRemoteDataStoreController @Inject()( | |
|
||
} | ||
|
||
// Register a datasource from the datastore as a dataset in the database. | ||
// This is called when adding remote virtual datasets (that should only exist in the database) | ||
// by the data store after exploration. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here, too, I wonder if the datastore needs to be involved in this communication at all 🤔 |
||
def registerDataSource(name: String, | ||
key: String, | ||
organizationId: String, | ||
directoryName: String, | ||
token: String): Action[DataSourceRegistrationInfo] = | ||
Action.async(validateJson[DataSourceRegistrationInfo]) { implicit request => | ||
dataStoreService.validateAccess(name, key) { dataStore => | ||
for { | ||
user <- bearerTokenService.userForToken(token) | ||
organization <- organizationDAO.findOne(organizationId)(GlobalAccessContext) ?~> Messages( | ||
"organization.notFound", | ||
organizationId) ~> NOT_FOUND | ||
_ <- Fox.fromBool(organization._id == user._organization) ?~> "notAllowed" ~> FORBIDDEN | ||
dataset <- datasetService.createVirtualDataset( | ||
directoryName, | ||
organizationId, | ||
dataStore, | ||
request.body.dataSource, | ||
request.body.folderId, | ||
user | ||
) | ||
} yield Ok(dataset._id.toString) | ||
} | ||
} | ||
|
||
def updateDataSource(name: String, key: String, datasetId: ObjectId): Action[DataSource] = | ||
Action.async(validateJson[DataSource]) { implicit request => | ||
dataStoreService.validateAccess(name, key) { _ => | ||
for { | ||
_ <- datasetDAO.findOne(datasetId)(GlobalAccessContext) ~> NOT_FOUND | ||
abstractDataSource = request.body.copy(dataLayers = request.body.dataLayers.map(AbstractDataLayer.from)) | ||
_ <- datasetDAO.updateDataSourceByDatasetId(datasetId, | ||
name, | ||
abstractDataSource.hashCode(), | ||
abstractDataSource, | ||
isUsable = true)(GlobalAccessContext) | ||
} yield Ok | ||
} | ||
} | ||
|
||
def jobExportProperties(name: String, key: String, jobId: ObjectId): Action[AnyContent] = Action.async { | ||
implicit request => | ||
dataStoreService.validateAccess(name, key) { _ => | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
package models.dataset | ||
|
||
import com.scalableminds.util.accesscontext.DBAccessContext | ||
import com.scalableminds.util.objectid.ObjectId | ||
import com.scalableminds.util.tools.{Fox, FoxImplicits} | ||
import com.scalableminds.webknossos.datastore.models.VoxelSize | ||
import com.scalableminds.webknossos.datastore.models.datasource._ | ||
import models.user.User | ||
import play.api.libs.json.{Json, OFormat} | ||
|
||
import javax.inject.Inject | ||
import scala.concurrent.ExecutionContext | ||
|
||
case class ComposeRequest( | ||
newDatasetName: String, | ||
targetFolderId: String, | ||
organizationId: String, | ||
voxelSize: VoxelSize, | ||
layers: Seq[ComposeRequestLayer] | ||
) | ||
|
||
object ComposeRequest { | ||
implicit val composeRequestFormat: OFormat[ComposeRequest] = Json.format[ComposeRequest] | ||
} | ||
case class ComposeRequestLayer( | ||
datasetId: String, | ||
sourceName: String, | ||
newName: String, | ||
transformations: Seq[CoordinateTransformation] | ||
) | ||
fm3 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
object ComposeRequestLayer { | ||
implicit val composeLayerFormat: OFormat[ComposeRequestLayer] = Json.format[ComposeRequestLayer] | ||
} | ||
|
||
class ComposeService @Inject()(datasetDAO: DatasetDAO, dataStoreDAO: DataStoreDAO, datasetService: DatasetService)( | ||
implicit ec: ExecutionContext) | ||
extends FoxImplicits { | ||
|
||
def composeDataset(composeRequest: ComposeRequest, user: User)( | ||
implicit ctx: DBAccessContext): Fox[(DataSource, ObjectId)] = | ||
for { | ||
_ <- isComposable(composeRequest) ?~> "Datasets are not composable, they are not on the same data store" | ||
fm3 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
dataSource <- createDatasource(composeRequest, composeRequest.newDatasetName, composeRequest.organizationId) | ||
dataStore <- dataStoreDAO.findOneWithUploadsAllowed | ||
dataset <- datasetService.createVirtualDataset(composeRequest.newDatasetName, | ||
composeRequest.organizationId, | ||
dataStore, | ||
dataSource, | ||
Some(composeRequest.targetFolderId), | ||
user) | ||
|
||
} yield (dataSource, dataset._id) | ||
|
||
private def getLayerFromComposeLayer(composeLayer: ComposeRequestLayer)( | ||
implicit ctx: DBAccessContext): Fox[DataLayer] = | ||
for { | ||
datasetIdValidated <- ObjectId.fromString(composeLayer.datasetId) ?~> "Invalid dataset ID" | ||
dataset <- datasetDAO.findOne(datasetIdValidated) ?~> "Dataset not found" | ||
ds <- datasetService.fullDataSourceFor(dataset) | ||
fm3 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
ds <- ds.toUsable.toFox ?~> "Dataset not usable" | ||
fm3 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
layer <- ds.dataLayers.find(_.name == composeLayer.sourceName).toFox | ||
applyCoordinateTransformations = (cOpt: Option[List[CoordinateTransformation]]) => | ||
cOpt match { | ||
case Some(c) => Some(c ++ composeLayer.transformations.toList) | ||
case None => Some(composeLayer.transformations.toList) | ||
} | ||
editedLayer: DataLayer <- layer match { | ||
case l: DataLayerWithMagLocators => | ||
Fox.successful( | ||
l.mapped(name = composeLayer.newName, | ||
coordinateTransformations = applyCoordinateTransformations(l.coordinateTransformations))) | ||
case _ => Fox.failure("Unsupported layer type for composition: " + layer.getClass.getSimpleName) | ||
} | ||
} yield editedLayer | ||
|
||
private def isComposable(composeRequest: ComposeRequest)(implicit ctx: DBAccessContext): Fox[Boolean] = | ||
// Check that all datasets are on the same data store | ||
// Using virtual datasets, we should also be able to compose datasets using non-file paths from different data | ||
// stores, however, the data store is only stored for each data set and not per mag. | ||
for { | ||
_ <- Fox.fromBool(composeRequest.layers.nonEmpty) ?~> "Cannot compose dataset with no layers" | ||
datasetIds = composeRequest.layers.map(_.datasetId).distinct | ||
datasetIdsValidated <- Fox.serialCombined(datasetIds.toList)(ObjectId.fromString(_)) ?~> "Invalid dataset ID" | ||
datasets <- Fox.serialCombined(datasetIdsValidated)(datasetDAO.findOne(_)) | ||
dataStores = datasets.map(_._dataStore) | ||
} yield { | ||
dataStores.distinct.size == 1 | ||
} | ||
|
||
private def createDatasource(composeRequest: ComposeRequest, datasetDirectoryName: String, organizationId: String)( | ||
implicit ctx: DBAccessContext): Fox[DataSource] = | ||
for { | ||
layers <- Fox.serialCombined(composeRequest.layers.toList)(getLayerFromComposeLayer(_)) | ||
dataSource = GenericDataSource( | ||
DataSourceId(datasetDirectoryName, organizationId), | ||
layers, | ||
composeRequest.voxelSize, | ||
None | ||
) | ||
|
||
} yield dataSource | ||
|
||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can use Action[ObjectId] and validateJson[ObjectId] directly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, it might make sense to turn around the deletion protocol so that the frontend talks to the wk side, and wk checks if isVirtual==false and if so asks the datastore to also delete on disk. But that could be a follow-up issue. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that would make more sense