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

Don't delete dataset in DB when annotations exist #7429

Merged
merged 20 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from 14 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
3 changes: 2 additions & 1 deletion CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
- Zarr datasets can now be directly uploaded to WEBKNOSSOS. [#7397](https://github.com/scalableminds/webknossos/pull/7397)
- Added support for reading uint24 rgb layers in datasets with zarr2/zarr3/n5/neuroglancerPrecomputed format, as used for voxelytics predictions. [#7413](https://github.com/scalableminds/webknossos/pull/7413)
- Adding a remote dataset can now be done by providing a Neuroglancer URI. [#7416](https://github.com/scalableminds/webknossos/pull/7416)
- Added a filter to the Task List->Stats column to quickly filter for tasks with "Prending", "In-Progress" or "Finished" instances. [#7430](https://github.com/scalableminds/webknossos/pull/7430)
- Added a filter to the Task List->Stats column to quickly filter for tasks with "Pending", "In-Progress" or "Finished" instances. [#7430](https://github.com/scalableminds/webknossos/pull/7430)

### Changed
- An appropriate error is returned when requesting an API version that is higher that the current version. [#7424](https://github.com/scalableminds/webknossos/pull/7424)
Expand All @@ -26,6 +26,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
- Fixed that trees with ids around 1023 were invisible on some systems. [#7443](https://github.com/scalableminds/webknossos/pull/7443)
- Fixed styling issues with the maintenance banner so that it no longer overlaps other menus, tabs, and buttons. [#7421](https://github.com/scalableminds/webknossos/pull/7421)
- Exploring HTTP uris of unknown hosts no longer causes an exception error message to be displayed. [#7422](https://github.com/scalableminds/webknossos/pull/7422)
- Datasets with annotations can now be deleted. The concerning annotations can no longer be viewed but still be downloaded. [#7429](https://github.com/scalableminds/webknossos/pull/7429)
- Fixed the initialization of the dark theme if it was active during page load. [#7446](https://github.com/scalableminds/webknossos/pull/7446)

### Removed
Expand Down
13 changes: 10 additions & 3 deletions app/controllers/WKRemoteDataStoreController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import mail.{MailchimpClient, MailchimpTag}

import javax.inject.Inject
import models.analytics.{AnalyticsService, UploadDatasetEvent}
import models.annotation.AnnotationDAO
import models.dataset._
import models.dataset.credential.CredentialDAO
import models.folder.FolderDAO
Expand Down Expand Up @@ -46,6 +47,7 @@ class WKRemoteDataStoreController @Inject()(
jobDAO: JobDAO,
multiUserDAO: MultiUserDAO,
credentialDAO: CredentialDAO,
annotationDAO: AnnotationDAO,
mailchimpClient: MailchimpClient,
slackNotificationService: SlackNotificationService,
conf: WkConf,
Expand Down Expand Up @@ -189,11 +191,16 @@ class WKRemoteDataStoreController @Inject()(
existingDataset = datasetDAO
.findOneByNameAndOrganizationName(datasourceId.name, datasourceId.team)(GlobalAccessContext)
.futureBox

_ <- existingDataset.flatMap {
case Full(dataset) =>
datasetDAO
.deleteDataset(dataset._id)
.flatMap(_ => usedStorageService.refreshStorageReportForDataset(dataset))
for {
annotationCount <- annotationDAO.countAllByDataset(dataset._id)(GlobalAccessContext)
_ = datasetDAO
.deleteDataset(dataset._id, onlyMarkAsDeleted = annotationCount > 0)
.flatMap(_ => usedStorageService.refreshStorageReportForDataset(dataset))
} yield ()

case _ => Fox.successful(())
}
} yield Ok
Expand Down
8 changes: 8 additions & 0 deletions app/models/annotation/Annotation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,14 @@ class AnnotationDAO @Inject()(sqlClient: SqlClient, annotationLayerDAO: Annotati
count <- countList.headOption
} yield count

def countAllByDataset(datasetId: ObjectId)(implicit ctx: DBAccessContext): Fox[Int] =
for {
accessQuery <- readAccessQuery
countList <- run(
q"select count(*) from $existingCollectionName where _dataset = $datasetId and $accessQuery".as[Int])
count <- countList.headOption
} yield count

// update operations

def insertOne(a: Annotation): Fox[Unit] = {
Expand Down
64 changes: 34 additions & 30 deletions app/models/dataset/Dataset.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,31 +34,30 @@ import utils.ObjectId

import scala.concurrent.ExecutionContext

case class Dataset(
_id: ObjectId,
_dataStore: String,
_organization: ObjectId,
_publication: Option[ObjectId],
_uploader: Option[ObjectId],
_folder: ObjectId,
inboxSourceHash: Option[Int],
defaultViewConfiguration: Option[DatasetViewConfiguration] = None,
adminViewConfiguration: Option[DatasetViewConfiguration] = None,
description: Option[String] = None,
displayName: Option[String] = None,
isPublic: Boolean,
isUsable: Boolean,
name: String,
scale: Option[Vec3Double],
sharingToken: Option[String],
status: String,
logoUrl: Option[String],
sortingKey: Instant = Instant.now,
details: Option[JsObject] = None,
tags: Set[String] = Set.empty,
created: Instant = Instant.now,
isDeleted: Boolean = false
) extends FoxImplicits {
case class Dataset(_id: ObjectId,
_dataStore: String,
_organization: ObjectId,
_publication: Option[ObjectId],
_uploader: Option[ObjectId],
_folder: ObjectId,
inboxSourceHash: Option[Int],
defaultViewConfiguration: Option[DatasetViewConfiguration] = None,
adminViewConfiguration: Option[DatasetViewConfiguration] = None,
description: Option[String] = None,
displayName: Option[String] = None,
isPublic: Boolean,
isUsable: Boolean,
name: String,
scale: Option[Vec3Double],
sharingToken: Option[String],
status: String,
logoUrl: Option[String],
sortingKey: Instant = Instant.now,
details: Option[JsObject] = None,
tags: Set[String] = Set.empty,
created: Instant = Instant.now,
isDeleted: Boolean = false)
extends FoxImplicits {

def urlEncodedName: String =
UriEncoding.encodePathSegment(name, "UTF-8")
Expand Down Expand Up @@ -93,6 +92,8 @@ class DatasetDAO @Inject()(sqlClient: SqlClient, datasetLayerDAO: DatasetLayerDA
protected def isDeletedColumn(x: Datasets): Rep[Boolean] = x.isdeleted

val unreportedStatus: String = "No longer available on datastore."
val deletedByUserStatus: String = "Deleted by user."
private val unreportedStatusList = List(unreportedStatus, deletedByUserStatus)

private def parseScaleOpt(literalOpt: Option[String]): Fox[Option[Vec3Double]] = literalOpt match {
case Some(literal) =>
Expand Down Expand Up @@ -288,7 +289,7 @@ class DatasetDAO @Inject()(sqlClient: SqlClient, datasetLayerDAO: DatasetLayerDA
lastUsedByUser = row._9,
status = row._10,
tags = parseArrayLiteral(row._11),
isUnreported = row._10 == unreportedStatus,
isUnreported = unreportedStatusList.contains(row._10),
))

private def buildSelectionPredicates(isActiveOpt: Option[Boolean],
Expand Down Expand Up @@ -333,8 +334,8 @@ class DatasetDAO @Inject()(sqlClient: SqlClient, datasetLayerDAO: DatasetLayerDA

private def buildIsUnreportedPredicate(isUnreportedOpt: Option[Boolean]): SqlToken =
isUnreportedOpt match {
case Some(true) => q"status = $unreportedStatus"
case Some(false) => q"status != $unreportedStatus"
case Some(true) => q"status = $unreportedStatus or status = $deletedByUserStatus"
case Some(false) => q"status != $unreportedStatus and status != $deletedByUserStatus"
case None => q"${true}"
}

Expand Down Expand Up @@ -559,14 +560,17 @@ class DatasetDAO @Inject()(sqlClient: SqlClient, datasetLayerDAO: DatasetLayerDA
} yield ()
}

def deleteDataset(datasetId: ObjectId): Fox[Unit] = {
def deleteDataset(datasetId: ObjectId, onlyMarkAsDeleted: Boolean = false): Fox[Unit] = {
val deleteResolutionsQuery =
q"delete from webknossos.dataSet_resolutions where _dataset = $datasetId".asUpdate
val deleteLayersQuery =
q"delete from webknossos.dataSet_layers where _dataset = $datasetId".asUpdate
val deleteAllowedTeamsQuery = q"delete from webknossos.dataSet_allowedTeams where _dataset = $datasetId".asUpdate
val deleteDatasetQuery =
q"delete from webknossos.datasets where _id = $datasetId".asUpdate
if (onlyMarkAsDeleted)
q"update webknossos.datasets set status = $deletedByUserStatus, isUsable = false where _id = $datasetId".asUpdate
else
q"delete from webknossos.datasets where _id = $datasetId".asUpdate

for {
_ <- run(
Expand Down
2 changes: 1 addition & 1 deletion app/models/dataset/DatasetService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class DatasetService @Inject()(organizationDAO: OrganizationDAO,
with LazyLogging {
private val unreportedStatus = datasetDAO.unreportedStatus
private val notYetUploadedStatus = "Not yet fully uploaded."
private val inactiveStatusList = List(unreportedStatus, notYetUploadedStatus)
private val inactiveStatusList = List(unreportedStatus, notYetUploadedStatus, datasetDAO.deletedByUserStatus)

def assertValidDatasetName(name: String): Fox[Unit] =
for {
Expand Down