-
Notifications
You must be signed in to change notification settings - Fork 23
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
Changes from 75 commits
dbe80eb
4ab38dc
242cf1d
3c139fe
5bb3997
1797a4e
55c3d5f
5cf5bfd
70b185e
3c4ae12
434b875
47daad4
9a8a5cd
0bd1c29
51cd0e4
488f5bd
b3294c4
8051729
3fb9613
1f0db40
eaaaaea
4ef393a
846b500
e7ed7f8
5e2912b
40e60c1
c091c0e
98892ca
d4ebd9f
b305e7c
a853611
113bb6e
e1bb660
eee0471
48665a7
9a8c6bb
6cdfa74
128f84c
ee5865d
050129b
539fc5d
51b1286
5d2bd7d
f235cd4
5ea7a34
df40a6c
0d1fe66
6728c71
0382aba
06cbd2f
050c3ac
b465021
6678725
47f96fc
1459f31
0e00f7b
aeba1f9
e9fcb24
b14a4d9
ebf16b7
d998e7f
4a75782
a748164
23cab35
2dafad4
f90d669
9fd95dd
1793ebf
93d9b6f
3c7b291
19adc1b
c0827bd
d624a27
dd3c3da
df5af16
0a607e8
c0c8233
6932f71
2eba0c9
0e5b29d
62b67e5
bfc14c3
d8be922
57dad17
aa08135
94a2dea
cacb0dd
2d4ebfa
ff72b1f
aa3fdeb
a3a92d2
e9fccae
984b889
a5184ee
3ea1f4d
30bb3e9
6080226
bc6739d
32f84d6
6508c59
de0768a
6fffde2
46224ba
c505be0
5dd4872
57311df
76c2a97
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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), | ||
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. Why is the "index" needed? If the data model is an array, the ordering should already be fixed, right? 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. 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 🤔 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.
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) | ||
|
@@ -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) | ||
|
@@ -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, | ||
|
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 | ||||||
|
@@ -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] | ||||||
} | ||||||
|
@@ -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) | ||||||
) | ||||||
|
||||||
|
@@ -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 | ||||||
|
@@ -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)) | ||||||
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.
Suggested change
|
||||||
} 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(_)))) | ||||||
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.
Suggested change
|
||||||
} 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"") | ||||||
|
@@ -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) | ||||||
|
@@ -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 | ||||||
|
@@ -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 | ||||||
|
||||||
|
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.
should use getOrElse or just orElse if an option is required. I’d also inline it below, just like
folderId.getOrElse(dataset._folder)