diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d5eda212da..9b040365216 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.md). - Users can now input floating point numbers into the rotation field in flight and oblique mode. These values will get rounded internally. [#4507](https://github.com/scalableminds/webknossos/pull/4507) - Deleting an empty tree group in the `Trees` tab no longer prompts for user confirmation. [#4506](https://github.com/scalableminds/webknossos/pull/4506) - Toggling the "Render missing data black" option now automatically reloads all layers making it unnecessary to reload the whole page. [#4516](https://github.com/scalableminds/webknossos/pull/4516) +- The "mappings" attribute of segmentation layers in datasource jsons can now be omitted. [#4532](https://github.com/scalableminds/webknossos/pull/4532) ### Fixed - Users only get tasks of datasets that they can access. [#4488](https://github.com/scalableminds/webknossos/pull/4488) diff --git a/app/models/binary/DataSet.scala b/app/models/binary/DataSet.scala index adf5e0b9eae..07661e81076 100755 --- a/app/models/binary/DataSet.scala +++ b/app/models/binary/DataSet.scala @@ -369,6 +369,7 @@ class DataSetDataLayerDAO @Inject()(sqlClient: SQLClient, dataSetResolutionsDAO: } yield { (row.largestsegmentid, row.mappings) match { case (Some(segmentId), Some(mappings)) => + val mappingsAsSet = parseArrayTuple(mappings).toSet Fox.successful( AbstractSegmentationLayer( row.name, @@ -377,7 +378,7 @@ class DataSetDataLayerDAO @Inject()(sqlClient: SQLClient, dataSetResolutionsDAO: resolutions.sortBy(_.maxDim), elementClass, segmentId, - parseArrayTuple(mappings).toSet, + if (mappingsAsSet.isEmpty) None else Some(mappingsAsSet), defaultViewConfigurationOpt.map(SegmentationLayerViewConfiguration.from) )) case (None, None) => @@ -419,10 +420,11 @@ class DataSetDataLayerDAO @Inject()(sqlClient: SQLClient, dataSetResolutionsDAO: def insertLayerQuery(_dataSet: ObjectId, layer: DataLayer) = layer match { case s: AbstractSegmentationLayer => { + val mappings = s.mappings.getOrElse(Set()) sqlu"""insert into webknossos.dataset_layers(_dataSet, name, category, elementClass, boundingBox, largestSegmentId, mappings, defaultViewConfiguration) values(${_dataSet.id}, ${s.name}, '#${s.category.toString}', '#${s.elementClass.toString}', '#${writeStructTuple(s.boundingBox.toSql.map(_.toString))}', ${s.largestSegmentId}, '#${writeArrayTuple( - s.mappings.map(sanitize(_)).toList)}', #${optionLiteral( + mappings.map(sanitize(_)).toList)}', #${optionLiteral( s.defaultViewConfiguration.map(d => Json.toJson(d).toString))})""" } case d: AbstractDataLayer => { diff --git a/frontend/javascripts/libs/datasource.schema.json b/frontend/javascripts/libs/datasource.schema.json index 41bea5c7882..2dbe00cc171 100644 --- a/frontend/javascripts/libs/datasource.schema.json +++ b/frontend/javascripts/libs/datasource.schema.json @@ -109,7 +109,7 @@ "largestSegmentId": { "type": "number", "minimum": 1 }, "mappings": { "type": "array", "items": { "type": "string" } } }, - "required": ["category", "largestSegmentId", "mappings"] + "required": ["category", "largestSegmentId"] } ] }, diff --git a/frontend/javascripts/test/snapshots/public/test-bundle/test/backend-snapshot-tests/datasets.e2e.js.md b/frontend/javascripts/test/snapshots/public/test-bundle/test/backend-snapshot-tests/datasets.e2e.js.md index 77306fb3653..fb1b7a9b9e4 100644 --- a/frontend/javascripts/test/snapshots/public/test-bundle/test/backend-snapshot-tests/datasets.e2e.js.md +++ b/frontend/javascripts/test/snapshots/public/test-bundle/test/backend-snapshot-tests/datasets.e2e.js.md @@ -206,7 +206,6 @@ Generated by [AVA](https://ava.li). category: 'segmentation', elementClass: 'uint16', largestSegmentId: 10000, - mappings: [], name: 'segmentation', resolutions: [], }, @@ -495,7 +494,6 @@ Generated by [AVA](https://ava.li). category: 'segmentation', elementClass: 'uint16', largestSegmentId: 10000, - mappings: [], name: 'segmentation', resolutions: [], }, diff --git a/frontend/javascripts/test/snapshots/public/test-bundle/test/backend-snapshot-tests/datasets.e2e.js.snap b/frontend/javascripts/test/snapshots/public/test-bundle/test/backend-snapshot-tests/datasets.e2e.js.snap index 1f5b19fca40..831c696bd07 100644 Binary files a/frontend/javascripts/test/snapshots/public/test-bundle/test/backend-snapshot-tests/datasets.e2e.js.snap and b/frontend/javascripts/test/snapshots/public/test-bundle/test/backend-snapshot-tests/datasets.e2e.js.snap differ diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/MappingProvider.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/MappingProvider.scala index 0728da57f45..bc262d91a09 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/MappingProvider.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/MappingProvider.scala @@ -29,8 +29,8 @@ object MappingProvider { val mappingFileExtension = "json" - def exploreMappings(layerDir: Path): Set[String] = - PathUtils + def exploreMappings(layerDir: Path): Option[Set[String]] = { + val mappingSet = PathUtils .listFiles(layerDir.resolve(MappingProvider.mappingsDir), PathUtils.fileExtensionFilter(MappingProvider.mappingFileExtension)) .map { paths => @@ -38,4 +38,6 @@ object MappingProvider { } .getOrElse(Nil) .toSet + if (mappingSet.isEmpty) None else Some(mappingSet) + } } diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/knossos/KnossosDataLayers.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/knossos/KnossosDataLayers.scala index 090d7642bcf..503b367b857 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/knossos/KnossosDataLayers.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/knossos/KnossosDataLayers.scala @@ -54,7 +54,7 @@ case class KnossosSegmentationLayer( name: String, sections: List[KnossosSection], elementClass: ElementClass.Value, - mappings: Set[String], + mappings: Option[Set[String]], largestSegmentId: Long, defaultViewConfiguration: Option[SegmentationLayerViewConfiguration] = None ) extends SegmentationLayer diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/wkw/WKWDataLayers.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/wkw/WKWDataLayers.scala index 2a81d144873..1d3f96e5eff 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/wkw/WKWDataLayers.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/wkw/WKWDataLayers.scala @@ -50,7 +50,7 @@ case class WKWSegmentationLayer( boundingBox: BoundingBox, wkwResolutions: List[WKWResolution], elementClass: ElementClass.Value, - mappings: Set[String], + mappings: Option[Set[String]], largestSegmentId: Long, defaultViewConfiguration: Option[SegmentationLayerViewConfiguration] = None ) extends SegmentationLayer diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/datasource/DataLayer.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/datasource/DataLayer.scala index e9374014304..56e089850b7 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/datasource/DataLayer.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/datasource/DataLayer.scala @@ -160,7 +160,7 @@ trait SegmentationLayerLike extends DataLayerLike { def largestSegmentId: Long - def mappings: Set[String] + def mappings: Option[Set[String]] def defaultViewConfiguration: Option[SegmentationLayerViewConfiguration] } @@ -261,7 +261,7 @@ case class AbstractSegmentationLayer( resolutions: List[Point3D], elementClass: ElementClass.Value, largestSegmentId: Long, - mappings: Set[String], + mappings: Option[Set[String]], defaultViewConfiguration: Option[SegmentationLayerViewConfiguration] = None ) extends SegmentationLayerLike diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/DataSourceImporter.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/DataSourceImporter.scala index dedc5042a3e..37807a9a6d6 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/DataSourceImporter.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/DataSourceImporter.scala @@ -86,6 +86,6 @@ trait DataSourceImporter { case Right(point) => point.maxDim } - protected def exploreMappings(baseDir: Path): Set[String] = MappingProvider.exploreMappings(baseDir) + protected def exploreMappings(baseDir: Path): Option[Set[String]] = MappingProvider.exploreMappings(baseDir) } diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/DataSourceService.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/DataSourceService.scala index ea1f6b7bf8b..ec43fe27577 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/DataSourceService.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/DataSourceService.scala @@ -109,7 +109,9 @@ class DataSourceService @Inject()( } def exploreMappings(organizationName: String, dataSetName: String, dataLayerName: String): Set[String] = - MappingProvider.exploreMappings(dataBaseDir.resolve(organizationName).resolve(dataSetName).resolve(dataLayerName)) + MappingProvider + .exploreMappings(dataBaseDir.resolve(organizationName).resolve(dataSetName).resolve(dataLayerName)) + .getOrElse(Set()) private def validateDataSource(dataSource: DataSource): Box[Unit] = { def Check(expression: Boolean, msg: String): Option[String] = if (!expression) Some(msg) else None diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/FallbackLayerAdapter.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/FallbackLayerAdapter.scala index 4b9527453b0..3495912c248 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/FallbackLayerAdapter.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/FallbackLayerAdapter.scala @@ -44,7 +44,7 @@ class FallbackLayerAdapter(primary: SegmentationLayer, fallback: SegmentationLay val largestSegmentId: Long = math.max(primary.largestSegmentId, fallback.largestSegmentId) - val mappings: Set[String] = primary.mappings + val mappings: Option[Set[String]] = primary.mappings lazy val bucketProvider: BucketProvider = new FallbackBucketProvider(primary, fallback) diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingLayer.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingLayer.scala index 2aae158dc34..899dd2157c2 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingLayer.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingLayer.scala @@ -48,7 +48,7 @@ case class VolumeTracingLayer( val bucketProvider: BucketProvider = volumeBucketProvider - val mappings: Set[String] = Set.empty + val mappings: Option[Set[String]] = None val resolutions: List[Point3D] = List(Point3D(1, 1, 1)) }