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

Folder And Dataset Metadata #7886

Merged
merged 107 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from 75 commits
Commits
Show all changes
107 commits
Select commit Hold shift + click to select a range
dbe80eb
Add dataset metadata input to details view in dashboard
MichaelBuessemeyer Jun 17, 2024
4ab38dc
update docs
hotzenklotz Jun 12, 2024
242cf1d
Improve mobile support of views (#7876)
MichaelBuessemeyer Jun 12, 2024
3c139fe
Mapping in default view config (#7858)
MichaelBuessemeyer Jun 13, 2024
5bb3997
Fix animation modal color layer validation (#7882)
hotzenklotz Jun 14, 2024
1797a4e
Validate animation job bounding box (#7883)
hotzenklotz Jun 17, 2024
55c3d5f
Merge branch 'master' of github.com:scalableminds/webknossos into fo…
MichaelBuessemeyer Jun 18, 2024
5cf5bfd
force serialization of dataset details prop in compact version of dat…
MichaelBuessemeyer Jun 18, 2024
70b185e
WIP adding details to folder
MichaelBuessemeyer Jun 18, 2024
3c4ae12
First version metadata support for folders
MichaelBuessemeyer Jun 19, 2024
434b875
fix folder listing query
MichaelBuessemeyer Jun 19, 2024
47daad4
finish frontend support for folder metadata
MichaelBuessemeyer Jun 19, 2024
9a8a5cd
default to undefined columnKey and not null to potentially prevent fr…
MichaelBuessemeyer Jun 20, 2024
0bd1c29
add evolution
MichaelBuessemeyer Jun 20, 2024
51cd0e4
update test db init data & folder snapshot test & bump schema version
MichaelBuessemeyer Jun 20, 2024
488f5bd
Merge branch 'master' of github.com:scalableminds/webknossos into fol…
MichaelBuessemeyer Jun 20, 2024
b3294c4
remove unused import
MichaelBuessemeyer Jun 20, 2024
8051729
also blur on property input
MichaelBuessemeyer Jun 20, 2024
3fb9613
only support / show current error
MichaelBuessemeyer Jun 20, 2024
1f0db40
improve css
MichaelBuessemeyer Jun 21, 2024
eaaaaea
WIP: use new json schema to save dataset & folder details and re-styl…
MichaelBuessemeyer Jun 26, 2024
4ef393a
Merge branch 'master' of github.com:scalableminds/webknossos into fol…
MichaelBuessemeyer Jun 27, 2024
846b500
fix backend to work with new type of details dataset / folder field &…
MichaelBuessemeyer Jun 27, 2024
e7ed7f8
WIP: improve dataset details table styling and add support for differ…
MichaelBuessemeyer Jun 27, 2024
5e2912b
details table for folders and datasets version 2
MichaelBuessemeyer Jun 28, 2024
40e60c1
WIP: adapt migration to new details format
MichaelBuessemeyer Jun 28, 2024
c091c0e
uncomment ci tests
MichaelBuessemeyer Jun 28, 2024
98892ca
mini migration fix
MichaelBuessemeyer Jun 28, 2024
d4ebd9f
- rename details to metadata
MichaelBuessemeyer Jul 2, 2024
b305e7c
fix schema
MichaelBuessemeyer Jul 2, 2024
a853611
remove unused backend imports
MichaelBuessemeyer Jul 2, 2024
113bb6e
only update metadata set of metadata table when folder / dataset changes
MichaelBuessemeyer Jul 2, 2024
e1bb660
ensure flushing updates on unmount of metadata table & increase debou…
MichaelBuessemeyer Jul 2, 2024
eee0471
remove accidental change
MichaelBuessemeyer Jul 4, 2024
48665a7
WIP apply styling feedback & refactor handling metadataentry type
MichaelBuessemeyer Jul 4, 2024
9a8c6bb
remove unused css
MichaelBuessemeyer Jul 4, 2024
6cdfa74
keep old dataset while updating & refetching in the dataset details view
MichaelBuessemeyer Jul 4, 2024
128f84c
remove unused code as search support for metadata entries is currentl…
MichaelBuessemeyer Jul 4, 2024
ee5865d
do not include metadata in dataset compact version
MichaelBuessemeyer Jul 4, 2024
050129b
Fix Dataset refetching
MichaelBuessemeyer Jul 5, 2024
539fc5d
have fix width for metadata table cell contents to ensure consistent…
MichaelBuessemeyer Jul 5, 2024
51b1286
enable selecting current folder of folder tree view as active details…
MichaelBuessemeyer Jul 5, 2024
5d2bd7d
do not have initial empty metadata row
MichaelBuessemeyer Jul 5, 2024
f235cd4
Merge branches 'folder-dataset-properties' and 'master' of github.com…
MichaelBuessemeyer Jul 17, 2024
5ea7a34
WIP: Apply styling update
MichaelBuessemeyer Jul 17, 2024
df40a6c
Finish next version
MichaelBuessemeyer Jul 17, 2024
0d1fe66
hopefully fix flickering bug in when two different metadata updates h…
MichaelBuessemeyer Jul 17, 2024
6728c71
Only have default metadata on species, brainRegion & acquisition when…
MichaelBuessemeyer Jul 17, 2024
0382aba
Merge branch 'master' of github.com:scalableminds/webknossos into fol…
MichaelBuessemeyer Jul 18, 2024
06cbd2f
also fill full metadata table width in case of an empty table
MichaelBuessemeyer Jul 18, 2024
050c3ac
allow changing prop name although it is a duplicate; do not save meta…
MichaelBuessemeyer Jul 18, 2024
b465021
Merge branch 'master' of github.com:scalableminds/webknossos into fol…
MichaelBuessemeyer Jul 19, 2024
6678725
clean up code for review
MichaelBuessemeyer Jul 19, 2024
47f96fc
add changelog & migration entry; rename evolution
MichaelBuessemeyer Jul 19, 2024
1459f31
also rename revision; add comments to revision; remove dev logging
MichaelBuessemeyer Jul 19, 2024
0e00f7b
Merge branch 'master' of github.com:scalableminds/webknossos into fol…
MichaelBuessemeyer Jul 23, 2024
aeba1f9
Apply PR Feedback
MichaelBuessemeyer Jul 23, 2024
e9fcb24
move metadata table to own file
MichaelBuessemeyer Jul 24, 2024
b14a4d9
only update when metadata changed & refactor code
MichaelBuessemeyer Jul 24, 2024
ebf16b7
fix updating the wrong dataset or folder with the newest metadata ver…
MichaelBuessemeyer Jul 24, 2024
d998e7f
allow multiple error rows & only update local metadata set if new fol…
MichaelBuessemeyer Jul 25, 2024
4a75782
update preview image
MichaelBuessemeyer Jul 25, 2024
a748164
fix color layer / segmentation layer switchero bug
MichaelBuessemeyer Jul 25, 2024
23cab35
remove periodic autosave an replace with explicit save via button or …
MichaelBuessemeyer Jul 25, 2024
2dafad4
Merge branch 'master' of github.com:scalableminds/webknossos into fol…
MichaelBuessemeyer Jul 25, 2024
f90d669
re add auto saving mechanism and do not have nested components to avo…
MichaelBuessemeyer Jul 26, 2024
9fd95dd
update snapshots
MichaelBuessemeyer Jul 26, 2024
1793ebf
Merge branch 'master' of github.com:scalableminds/webknossos into fol…
MichaelBuessemeyer Jul 29, 2024
93d9b6f
refactor code & remove `useWillUnmount` which sent outdated metadata …
MichaelBuessemeyer Jul 29, 2024
3c7b291
Merge branch 'master' of github.com:scalableminds/webknossos into fol…
MichaelBuessemeyer Jul 29, 2024
19adc1b
remove unnecessary dependency from useEffect accidentally added in a …
MichaelBuessemeyer Jul 29, 2024
c0827bd
include metadata in full dataset update route
MichaelBuessemeyer Aug 6, 2024
d624a27
fix full update dataset route for metadata support
MichaelBuessemeyer Aug 7, 2024
dd3c3da
remove option to update tags (as they will no longer be rendered in t…
MichaelBuessemeyer Aug 7, 2024
df5af16
Merge branch 'master' into folder-dataset-properties
MichaelBuessemeyer Aug 7, 2024
0a607e8
remove index from initial metdata added to datasets with publication
MichaelBuessemeyer Aug 9, 2024
c0c8233
apply pr feedback (testing pending)
MichaelBuessemeyer Aug 9, 2024
6932f71
lint frontend
MichaelBuessemeyer Aug 9, 2024
2eba0c9
Merge branch 'master' into folder-dataset-properties
MichaelBuessemeyer Aug 12, 2024
0e5b29d
do not send updates while a row of the metadata table is focused
MichaelBuessemeyer Aug 12, 2024
62b67e5
avoid lost isSaving updates due to changes to `focusedRow` state whil…
MichaelBuessemeyer Aug 12, 2024
bfc14c3
fix sending double updates on unmount
MichaelBuessemeyer Aug 13, 2024
d8be922
Merge branch 'master' of github.com:scalableminds/webknossos into fol…
MichaelBuessemeyer Aug 14, 2024
57dad17
apply pr feedback
MichaelBuessemeyer Aug 14, 2024
aa08135
Merge branch 'master' of github.com:scalableminds/webknossos into fol…
MichaelBuessemeyer Aug 15, 2024
94a2dea
migrate existing tags into metadata
MichaelBuessemeyer Aug 15, 2024
cacb0dd
remove unwanted additional wrapping array around migrated tags in met…
MichaelBuessemeyer Aug 15, 2024
2d4ebfa
add undo migrating tags to metadata to revision
MichaelBuessemeyer Aug 15, 2024
ff72b1f
fix revision schema
MichaelBuessemeyer Aug 15, 2024
aa3fdeb
give delete metadata entry button some more space; align key input to…
MichaelBuessemeyer Aug 15, 2024
a3a92d2
Merge branch 'master' into folder-dataset-properties
fm3 Aug 20, 2024
e9fccae
Merge branch 'master' of github.com:scalableminds/webknossos into fol…
MichaelBuessemeyer Aug 21, 2024
984b889
update schema migration version
MichaelBuessemeyer Aug 21, 2024
a5184ee
apply feedback
MichaelBuessemeyer Aug 21, 2024
3ea1f4d
readd tag support in frontend (and backend)
MichaelBuessemeyer Aug 22, 2024
30bb3e9
fix backend
MichaelBuessemeyer Aug 22, 2024
6080226
make metadata non nullable
MichaelBuessemeyer Aug 22, 2024
bc6739d
update snapshots tests
MichaelBuessemeyer Aug 23, 2024
32f84d6
Merge branch 'master' of github.com:scalableminds/webknossos into fol…
MichaelBuessemeyer Aug 23, 2024
6508c59
fix metadatatype type definition
MichaelBuessemeyer Aug 23, 2024
de0768a
Merge branch 'master' into folder-dataset-properties
MichaelBuessemeyer Aug 26, 2024
6fffde2
Merge branch 'master' into folder-dataset-properties
philippotto Aug 26, 2024
46224ba
fix reversion against default null values of re-added details column
MichaelBuessemeyer Aug 27, 2024
c505be0
Merge branch 'master' into folder-dataset-properties
MichaelBuessemeyer Aug 27, 2024
5dd4872
Merge branch 'master' into folder-dataset-properties
philippotto Aug 28, 2024
57311df
Merge branch 'master' into folder-dataset-properties
fm3 Aug 28, 2024
76c2a97
Merge branch 'master' into folder-dataset-properties
MichaelBuessemeyer Aug 28, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
### Added
- WEBKNOSSOS now automatically searches in subfolder / sub-collection identifiers for valid datasets in case a provided link to a remote dataset does not directly point to a dataset. [#7912](https://github.com/scalableminds/webknossos/pull/7912)
- Added the option to move a bounding box via dragging while pressing ctrl / meta. [#7892](https://github.com/scalableminds/webknossos/pull/7892)
- Added the option to add metadata entries to datasets and folders. The metadata can be viewed and edited in the dashboard in the right details tab.[#7886](https://github.com/scalableminds/webknossos/pull/7886)
- Added route `/import?url=<url_to_datasource>` to automatically import and view remote datasets. [#7844](https://github.com/scalableminds/webknossos/pull/7844)
- Added that newly created, modified and clicked on bounding boxes are now highlighted and scrolled into view, while the bounding box tool is active. [#7935](https://github.com/scalableminds/webknossos/pull/7935)
- The configured unit in the dataset upload view is now passed to the convert_to_wkw worker job. [#7970](https://github.com/scalableminds/webknossos/pull/7970)
Expand Down
1 change: 1 addition & 0 deletions MIGRATIONS.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ User-facing changes are documented in the [changelog](CHANGELOG.released.md).
[Commits](https://github.com/scalableminds/webknossos/compare/24.07.0...HEAD)

### Postgres Evolutions:
- [118-add-metadata-to-folders-and-datasets.sql](conf/evolutions/118-add-metadata-to-folders-and-datasets.sql)
21 changes: 13 additions & 8 deletions app/controllers/DatasetController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ case class DatasetUpdateParameters(
displayName: Option[Option[String]] = Some(None),
sortingKey: Option[Instant],
isPublic: Option[Boolean],
tags: Option[List[String]],
metadata: Option[JsArray],
folderId: Option[ObjectId]
)

Expand Down Expand Up @@ -79,6 +79,7 @@ class DatasetController @Inject()(userService: UserService,
(__ \ "sortingKey").readNullable[Instant] and
(__ \ "isPublic").read[Boolean] and
(__ \ "tags").read[List[String]] and
(__ \ "metadata").readNullable[JsArray] and
(__ \ "folderId").readNullable[ObjectId]).tupled

def removeFromThumbnailCache(organizationName: String, datasetName: String): Action[AnyContent] =
Expand Down Expand Up @@ -298,17 +299,21 @@ class DatasetController @Inject()(userService: UserService,
def update(organizationName: String, datasetName: String): Action[JsValue] =
sil.SecuredAction.async(parse.json) { implicit request =>
withJsonBodyUsing(datasetPublicReads) {
case (description, displayName, sortingKey, isPublic, tags, folderId) =>
case (description, displayName, sortingKey, isPublic, tags, metadata, folderId) =>
for {
dataset <- datasetDAO.findOneByNameAndOrganization(datasetName, request.identity._organization) ?~> notFoundMessage(
datasetName) ~> NOT_FOUND
maybeUpdatedMetadata = if (metadata.isDefined) metadata else dataset.metadata
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should use getOrElse or just orElse if an option is required. I’d also inline it below, just like folderId.getOrElse(dataset._folder)

_ <- Fox.assertTrue(datasetService.isEditableBy(dataset, Some(request.identity))) ?~> "notAllowed" ~> FORBIDDEN
_ <- datasetDAO.updateFields(dataset._id,
description,
displayName,
sortingKey.getOrElse(dataset.created),
isPublic,
folderId.getOrElse(dataset._folder))
_ <- datasetDAO.updateFields(
dataset._id,
description,
displayName,
sortingKey.getOrElse(dataset.created),
isPublic,
maybeUpdatedMetadata,
folderId.getOrElse(dataset._folder)
)
_ <- datasetDAO.updateTags(dataset._id, tags)
updated <- datasetDAO.findOneByNameAndOrganization(datasetName, request.identity._organization)
_ = analyticsService.track(ChangeDatasetSettingsEvent(request.identity, updated))
Expand Down
5 changes: 3 additions & 2 deletions app/controllers/FolderController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import models.folder.{Folder, FolderDAO, FolderParameters, FolderService}
import models.organization.OrganizationDAO
import models.team.{TeamDAO, TeamService}
import models.user.UserService
import play.api.libs.json.Json
import play.api.libs.json.{JsArray, Json}
import play.api.mvc.{Action, AnyContent, PlayBodyParsers}
import security.WkEnv
import utils.ObjectId
Expand Down Expand Up @@ -53,6 +53,7 @@ class FolderController @Inject()(
_ <- folderDAO.findOne(idValidated) ?~> "folder.notFound"
- <- Fox.assertTrue(folderDAO.isEditable(idValidated)) ?~> "folder.update.notAllowed" ~> FORBIDDEN
_ <- folderService.assertValidFolderName(params.name)
_ <- folderDAO.updateMetadata(idValidated, params.metadata)
_ <- folderDAO.updateName(idValidated, params.name) ?~> "folder.update.name.failed"
_ <- folderService
.updateAllowedTeams(idValidated, params.allowedTeams, request.identity) ?~> "folder.update.teams.failed"
Expand Down Expand Up @@ -103,7 +104,7 @@ class FolderController @Inject()(
for {
parentIdValidated <- ObjectId.fromString(parentId)
_ <- folderService.assertValidFolderName(name)
newFolder = Folder(ObjectId.generate, name)
newFolder = Folder(ObjectId.generate, name, JsArray.empty)
_ <- folderDAO.findOne(parentIdValidated) ?~> "folder.notFound"
_ <- folderDAO.insertAsChild(parentIdValidated, newFolder) ?~> "folder.create.failed"
organization <- organizationDAO.findOne(request.identity._organization) ?~> "folder.notFound"
Expand Down
5 changes: 3 additions & 2 deletions app/controllers/InitialDataController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import models.task.{TaskType, TaskTypeDAO}
import models.team._
import models.user._
import net.liftweb.common.{Box, Full}
import play.api.libs.json.Json
import play.api.libs.json.{JsArray, Json}
import utils.{ObjectId, StoreModules, WkConf}

import javax.inject.Inject
Expand Down Expand Up @@ -169,7 +169,8 @@ Samplecountry
private def insertRootFolder(): Fox[Unit] =
folderDAO.findOne(defaultOrganization._rootFolder).futureBox.flatMap {
case Full(_) => Fox.successful(())
case _ => folderDAO.insertAsRoot(Folder(defaultOrganization._rootFolder, folderService.defaultRootName))
case _ =>
folderDAO.insertAsRoot(Folder(defaultOrganization._rootFolder, folderService.defaultRootName, JsArray.empty))
}

private def insertDefaultUser(userEmail: String,
Expand Down
30 changes: 17 additions & 13 deletions app/models/dataset/Dataset.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ case class Dataset(_id: ObjectId,
status: String,
logoUrl: Option[String],
sortingKey: Instant = Instant.now,
details: Option[JsObject] = None,
metadata: Option[JsArray] = None,
tags: List[String] = List.empty,
created: Instant = Instant.now,
isDeleted: Boolean = false)
Expand Down Expand Up @@ -116,7 +116,7 @@ class DatasetDAO @Inject()(sqlClient: SqlClient, datasetLayerDAO: DatasetLayerDA
JsonHelper.parseAndValidateJson[DatasetViewConfiguration](_))
adminViewConfigurationOpt <- Fox.runOptional(r.adminviewconfiguration)(
JsonHelper.parseAndValidateJson[DatasetViewConfiguration](_))
details <- Fox.runOptional(r.details)(JsonHelper.parseAndValidateJson[JsObject](_))
metadata <- Fox.runOptional(r.metadata)(JsonHelper.parseAndValidateJson[JsArray](_))
} yield {
Dataset(
ObjectId(r._Id),
Expand All @@ -138,7 +138,7 @@ class DatasetDAO @Inject()(sqlClient: SqlClient, datasetLayerDAO: DatasetLayerDA
r.status,
r.logourl,
Instant.fromSql(r.sortingkey),
details,
metadata,
parseArrayLiteral(r.tags).sorted,
Instant.fromSql(r.created),
r.isdeleted
Expand Down Expand Up @@ -475,8 +475,8 @@ class DatasetDAO @Inject()(sqlClient: SqlClient, datasetLayerDAO: DatasetLayerDA
params.displayName.map(v => q"displayName = $v"),
params.sortingKey.map(v => q"sortingKey = $v"),
params.isPublic.map(v => q"isPublic = $v"),
params.tags.map(v => q"tags = $v"),
params.folderId.map(v => q"_folder = $v"),
params.metadata.map(v => q"metadata = $v"),
).flatten
if (setQueries.isEmpty) {
Fox.successful(())
Expand All @@ -493,18 +493,22 @@ class DatasetDAO @Inject()(sqlClient: SqlClient, datasetLayerDAO: DatasetLayerDA
}
}

def updateFields(_id: ObjectId,
def updateFields(datasetId: ObjectId,
description: Option[String],
displayName: Option[String],
sortingKey: Instant,
isPublic: Boolean,
metadata: Option[JsArray],
folderId: ObjectId)(implicit ctx: DBAccessContext): Fox[Unit] = {
val query = for { row <- Datasets if notdel(row) && row._Id === _id.id } yield
(row.description, row.displayname, row.sortingkey, row.ispublic, row._Folder)
for {
_ <- assertUpdateAccess(_id)
_ <- run(query.update(description, displayName, sortingKey.toSql, isPublic, folderId.toString))
} yield ()
val updateParameters = new DatasetUpdateParameters(
description = Some(description),
displayName = Some(displayName),
sortingKey = Some(sortingKey),
isPublic = Some(isPublic),
metadata = metadata,
folderId = Some(folderId)
)
updatePartial(datasetId, updateParameters)
MichaelBuessemeyer marked this conversation as resolved.
Show resolved Hide resolved
}

def updateTags(id: ObjectId, tags: List[String])(implicit ctx: DBAccessContext): Fox[Unit] =
Expand Down Expand Up @@ -548,7 +552,7 @@ class DatasetDAO @Inject()(sqlClient: SqlClient, datasetLayerDAO: DatasetLayerDA
inboxSourceHash, defaultViewConfiguration, adminViewConfiguration,
description, displayName, isPublic, isUsable,
name, voxelSizeFactor, voxelSizeUnit, status,
sharingToken, sortingKey, details, tags,
sharingToken, sortingKey, metadata, tags,
created, isDeleted
)
VALUES(
Expand All @@ -557,7 +561,7 @@ class DatasetDAO @Inject()(sqlClient: SqlClient, datasetLayerDAO: DatasetLayerDA
${d.inboxSourceHash}, $defaultViewConfiguration, $adminViewConfiguration,
${d.description}, ${d.displayName}, ${d.isPublic}, ${d.isUsable},
${d.name}, ${d.voxelSize.map(_.factor)}, ${d.voxelSize.map(_.unit)}, ${d.status.take(1024)},
${d.sharingToken}, ${d.sortingKey}, ${d.details}, ${d.tags},
${d.sharingToken}, ${d.sortingKey}, ${d.metadata}, ${d.tags},
${d.created}, ${d.isDeleted}
)""".asUpdate)
} yield ()
Expand Down
15 changes: 11 additions & 4 deletions app/models/dataset/DatasetService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,15 @@ class DatasetService @Inject()(organizationDAO: OrganizationDAO,
): Fox[Dataset] = {
implicit val ctx: DBAccessContext = GlobalAccessContext
val newId = ObjectId.generate
val details =
Json.obj("species" -> "species name", "brainRegion" -> "brain region", "acquisition" -> "acquisition method")
val metadata =
if (publication.isDefined)
Json.arr(
Json.obj("type" -> "string", "key" -> "species", "value" -> "species name", "index" -> 0),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the "index" needed? If the data model is an array, the ordering should already be fixed, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, you are right. I added it to support the potential free rearrangement of the metadata entries.

But you are right, the entry order can be stored via the array index. In the frontend the entries have an index to make the code easier. -> I could remove the index and always re-add it in the frontend 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could remove the index and always re-add it in the frontend 🤔

Its now implemented that way

Json.obj("type" -> "string", "key" -> "brainRegion", "value" -> "brain region", "index" -> 1),
Json.obj("type" -> "string", "key" -> "acquisition", "value" -> "acquisition method", "index" -> 2)
)
else Json.arr()

val dataSourceHash = if (dataSource.isUsable) Some(dataSource.hashCode()) else None
for {
organization <- organizationDAO.findOneByName(owningOrganization)
Expand All @@ -102,7 +109,7 @@ class DatasetService @Inject()(organizationDAO: OrganizationDAO,
sharingToken = None,
status = dataSource.statusOpt.getOrElse(""),
logoUrl = None,
details = publication.map(_ => details)
metadata = Some(metadata)
)
_ <- datasetDAO.insertOne(dataset)
_ <- datasetDataLayerDAO.updateLayers(newId, dataSource)
Expand Down Expand Up @@ -353,7 +360,7 @@ class DatasetService @Inject()(organizationDAO: OrganizationDAO,
"lastUsedByUser" -> lastUsedByUser,
"logoUrl" -> logoUrl,
"sortingKey" -> dataset.sortingKey,
"details" -> dataset.details,
"metadata" -> dataset.metadata,
"isUnreported" -> Json.toJson(isUnreported(dataset)),
"tags" -> dataset.tags,
"folderId" -> dataset._folder,
Expand Down
53 changes: 36 additions & 17 deletions app/models/folder/Folder.scala
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package models.folder

import com.scalableminds.util.accesscontext.DBAccessContext
import com.scalableminds.util.tools.Fox
import com.scalableminds.util.tools.{Fox, JsonHelper}
import com.scalableminds.util.tools.Fox.{bool2Fox, option2Fox}
import com.scalableminds.webknossos.schema.Tables._
import com.typesafe.scalalogging.LazyLogging
import models.organization.{Organization, OrganizationDAO}
import models.team.{TeamDAO, TeamService}
import models.user.User
import play.api.libs.json.{JsObject, Json, OFormat}
import play.api.libs.json.{JsArray, JsObject, Json, OFormat}
import slick.jdbc.PostgresProfile.api._
import slick.lifted.Rep
import slick.sql.SqlAction
Expand All @@ -19,11 +19,11 @@ import javax.inject.Inject
import scala.annotation.tailrec
import scala.concurrent.ExecutionContext

case class Folder(_id: ObjectId, name: String)
case class Folder(_id: ObjectId, name: String, metadata: JsArray)

case class FolderWithParent(_id: ObjectId, name: String, _parent: Option[ObjectId])
case class FolderWithParent(_id: ObjectId, name: String, metadata: JsArray, _parent: Option[ObjectId])

case class FolderParameters(name: String, allowedTeams: List[ObjectId])
case class FolderParameters(name: String, allowedTeams: List[ObjectId], metadata: JsArray)
object FolderParameters {
implicit val jsonFormat: OFormat[FolderParameters] = Json.format[FolderParameters]
}
Expand All @@ -48,17 +48,21 @@ class FolderService @Inject()(teamDAO: TeamDAO,
teamService.publicWrites(t, requestingUserOrganization)) ?~> "dataset.list.teamWritesFailed"
isEditable <- folderDAO.isEditable(folder._id)
} yield
Json.obj("id" -> folder._id,
"name" -> folder.name,
"allowedTeams" -> teamsJs,
"allowedTeamsCumulative" -> teamsCumulativeJs,
"isEditable" -> isEditable)
Json.obj(
"id" -> folder._id,
"name" -> folder.name,
"metadata" -> folder.metadata,
"allowedTeams" -> teamsJs,
"allowedTeamsCumulative" -> teamsCumulativeJs,
"isEditable" -> isEditable
)

def publicWritesWithParent(folderWithParent: FolderWithParent, allEditableIds: Set[ObjectId]): JsObject =
Json.obj(
"id" -> folderWithParent._id,
"name" -> folderWithParent.name,
"parent" -> folderWithParent._parent,
"metadata" -> folderWithParent.metadata,
"isEditable" -> allEditableIds.contains(folderWithParent._id)
)

Expand Down Expand Up @@ -117,7 +121,7 @@ class FolderService @Inject()(teamDAO: TeamDAO,
remainingPathNames match {
case pathNamesHead :: pathNamesTail =>
for {
newFolder <- Fox.successful(Folder(ObjectId.generate, pathNamesHead))
newFolder <- Fox.successful(Folder(ObjectId.generate, pathNamesHead, JsArray.empty))
_ <- folderDAO.insertAsChild(parentFolderId, newFolder)
folderId <- createMissingFoldersForPathNames(newFolder._id, pathNamesTail)
} yield folderId
Expand All @@ -133,10 +137,19 @@ class FolderDAO @Inject()(sqlClient: SqlClient)(implicit ec: ExecutionContext)
protected def isDeletedColumn(x: Folders): Rep[Boolean] = x.isdeleted

protected def parse(r: FoldersRow): Fox[Folder] =
Fox.successful(Folder(ObjectId(r._Id), r.name))
for {
metadata <- parseMetadata(r.metadata)
folder <- Fox.successful(Folder(ObjectId(r._Id), r.name, metadata))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
folder <- Fox.successful(Folder(ObjectId(r._Id), r.name, metadata))
folder = Folder(ObjectId(r._Id), r.name, metadata)

} yield folder

private def parseWithParent(t: (String, String, Option[String], Option[String])): Fox[FolderWithParent] =
for {
metadata <- parseMetadata(t._3)
folderWithParent <- Fox.successful(FolderWithParent(ObjectId(t._1), t._2, metadata, t._4.map(ObjectId(_))))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
folderWithParent <- Fox.successful(FolderWithParent(ObjectId(t._1), t._2, metadata, t._4.map(ObjectId(_))))
folderWithParent = FolderWithParent(ObjectId(t._1), t._2, metadata, t._4.map(ObjectId(_)))

} yield folderWithParent

private def parseWithParent(t: (String, String, Option[String])): Fox[FolderWithParent] =
Fox.successful(FolderWithParent(ObjectId(t._1), t._2, t._3.map(ObjectId(_))))
private def parseMetadata(literal: Option[String]): Fox[JsArray] =
JsonHelper.parseAndValidateJson[JsArray](literal.getOrElse("[]"))

override protected def readAccessQ(requestingUserId: ObjectId): SqlToken =
readAccessQWithPrefix(requestingUserId, q"")
Expand Down Expand Up @@ -246,6 +259,12 @@ class FolderDAO @Inject()(sqlClient: SqlClient)(implicit ec: ExecutionContext)
_ <- run(q"UPDATE webknossos.folders SET name = $name WHERE _id = $folderId".asUpdate)
} yield ()

def updateMetadata(folderId: ObjectId, metadata: JsArray)(implicit ctx: DBAccessContext): Fox[Unit] =
for {
_ <- assertUpdateAccess(folderId)
_ <- run(q"UPDATE webknossos.folders SET metadata = $metadata WHERE _id = $folderId".asUpdate)
} yield ()

def findAllEditableIds(implicit ctx: DBAccessContext): Fox[List[ObjectId]] =
for {
updateAccessQuery <- accessQueryFromAccessQ(updateAccessQ)
Expand All @@ -269,7 +288,7 @@ class FolderDAO @Inject()(sqlClient: SqlClient)(implicit ec: ExecutionContext)
for {
accessQueryWithPrefix <- accessQueryFromAccessQWithPrefix(readAccessQWithPrefix, prefix = q"f.")
accessQuery <- readAccessQuery
rows <- run(q"""SELECT f._id, f.name, fp._ancestor
rows <- run(q"""SELECT f._id, f.name, f.metadata, fp._ancestor
FROM webknossos.folders_ f
JOIN webknossos.folder_paths fp -- join to find immediate parent, this will also kick out self
ON f._id = fp._descendant
Expand All @@ -278,11 +297,11 @@ class FolderDAO @Inject()(sqlClient: SqlClient)(implicit ec: ExecutionContext)
FROM webknossos.folder_paths
WHERE _ancestor = $folderId)
AND $accessQueryWithPrefix
UNION ALL SELECT _id, name, NULL -- find self again, with no parent
UNION ALL SELECT _id, name, metadata, NULL -- find self again, with no parent
FROM webknossos.folders_
WHERE _id = $folderId
AND $accessQuery
""".as[(String, String, Option[String])])
""".as[(String, String, Option[String], Option[String])])
parsed <- Fox.combined(rows.toList.map(parseWithParent))
} yield parsed

Expand Down
4 changes: 2 additions & 2 deletions app/models/organization/OrganizationService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import models.dataset.{DataStore, DataStoreDAO}
import models.folder.{Folder, FolderDAO, FolderService}
import models.team.{PricingPlan, Team, TeamDAO}
import models.user.{Invite, MultiUserDAO, User, UserDAO, UserService}
import play.api.libs.json.{JsObject, Json}
import play.api.libs.json.{JsArray, JsObject, Json}
import utils.{ObjectId, WkConf}

import scala.concurrent.{ExecutionContext, Future}
Expand Down Expand Up @@ -111,7 +111,7 @@ class OrganizationService @Inject()(organizationDAO: OrganizationDAO,
_ <- bool2Fox(existingOrganization.isEmpty) ?~> "organization.name.alreadyInUse"
initialPricingParameters = if (conf.Features.isWkorgInstance) (PricingPlan.Basic, Some(3), Some(50000000000L))
else (PricingPlan.Custom, None, None)
organizationRootFolder = Folder(ObjectId.generate, folderService.defaultRootName)
organizationRootFolder = Folder(ObjectId.generate, folderService.defaultRootName, JsArray.empty)

organization = Organization(
ObjectId.generate,
Expand Down
Loading