diff --git a/core/src/main/scala/se/lu/nateko/cp/meta/core/data/DataObject.scala b/core/src/main/scala/se/lu/nateko/cp/meta/core/data/DataObject.scala index 47e77a683..1142977c2 100644 --- a/core/src/main/scala/se/lu/nateko/cp/meta/core/data/DataObject.scala +++ b/core/src/main/scala/se/lu/nateko/cp/meta/core/data/DataObject.scala @@ -86,7 +86,7 @@ case class StationTimeSeriesMeta( productionInfo: Option[DataProduction], nRows: Option[Int], coverage: Option[GeoFeature], - columns: Option[Seq[VarMeta]] + columns: Seq[VarMeta] ) case class ValueType(self: UriResource, quantityKind: Option[UriResource], unit: Option[String]) @@ -108,7 +108,7 @@ case class SpatioTemporalMeta( station: Option[Station], samplingHeight: Option[Float], productionInfo: DataProduction, - variables: Option[Seq[VarMeta]] + variables: Seq[VarMeta] ){ def acquisition: Option[DataAcquisition] = station.map{ DataAcquisition(_, None, Some(temporal.interval), None, None, samplingHeight) @@ -164,11 +164,12 @@ case class DataObject( l2 => l2.coverage.orElse(l2.acquisition.coverage) ) + val cols = specificInfo.fold( + l3 => l3.variables, + l2 => l2.columns + ) + val varsAndPosits = for - cols <- specificInfo.fold( - l3 => l3.variables, - l2 => l2.columns - ).toSeq col <- cols deps <- col.instrumentDeployments.toSeq dep <- deps diff --git a/src/main/scala/se/lu/nateko/cp/meta/UploadDtos.scala b/src/main/scala/se/lu/nateko/cp/meta/UploadDtos.scala index d42b5b1b8..be4ece372 100644 --- a/src/main/scala/se/lu/nateko/cp/meta/UploadDtos.scala +++ b/src/main/scala/se/lu/nateko/cp/meta/UploadDtos.scala @@ -86,7 +86,7 @@ case class SpatioTemporalDto( forStation: Option[URI], samplingHeight: Option[Float], customLandingPage: Option[URI], - variables: Option[Seq[String]] + variables: Seq[String] ) case class DataProductionDto( diff --git a/src/main/scala/se/lu/nateko/cp/meta/services/UploadDtoReader.scala b/src/main/scala/se/lu/nateko/cp/meta/services/UploadDtoReader.scala index d37ebc102..6e2e96a71 100644 --- a/src/main/scala/se/lu/nateko/cp/meta/services/UploadDtoReader.scala +++ b/src/main/scala/se/lu/nateko/cp/meta/services/UploadDtoReader.scala @@ -47,7 +47,7 @@ object UploadDtoReader{ samplingHeight = l3.samplingHeight, production = dataProductionToDto(l3.productionInfo), customLandingPage = dobj.accessUrl.filterNot(uri => uri.getPath.endsWith(dobj.hash.id)), - variables = l3.variables.map(_.map(_.model.uri.toString.split('/').last)) + variables = l3.variables.map(_.model.uri.toString.split('/').last) )) case Right(l2) => Right(StationTimeSeriesDto( station = l2.acquisition.station.org.self.uri, diff --git a/src/main/scala/se/lu/nateko/cp/meta/services/citation/AttributionProvider.scala b/src/main/scala/se/lu/nateko/cp/meta/services/citation/AttributionProvider.scala index cdca67efb..a1dcb2329 100644 --- a/src/main/scala/se/lu/nateko/cp/meta/services/citation/AttributionProvider.scala +++ b/src/main/scala/se/lu/nateko/cp/meta/services/citation/AttributionProvider.scala @@ -65,10 +65,9 @@ final class AttributionProvider(vocab: CpVocab, val metaVocab: CpmetaVocab) exte if(dobj.specification.theme.self.uri === vocab.atmoTheme) memb => (memb.role.weight.isDefined && { val speciesOk = for( extra <- memb.role.extra; - l2 <- dobj.specificInfo.toOption; - cols <- l2.columns + l2 <- dobj.specificInfo.toOption ) yield{ - val colLabels = cols.map(_.label.toLowerCase) + val colLabels = l2.columns.map(_.label.toLowerCase) extra.split(',').map(_.trim.toLowerCase).exists(species => colLabels.exists(_.contains(species)) || dobj.specification.self.label.getOrElse("").toLowerCase.contains(species) diff --git a/src/main/scala/se/lu/nateko/cp/meta/services/citation/CitationMaker.scala b/src/main/scala/se/lu/nateko/cp/meta/services/citation/CitationMaker.scala index dc3f5e42d..d9f5205ac 100644 --- a/src/main/scala/se/lu/nateko/cp/meta/services/citation/CitationMaker.scala +++ b/src/main/scala/se/lu/nateko/cp/meta/services/citation/CitationMaker.scala @@ -177,9 +177,11 @@ class CitationMaker( val height = acq.samplingHeight.fold("")(sh => s" ($sh m)") val vars = if dobj.specification.self.uri === vocab.atmGhgProdSpec then - stationTs.columns.fold("")(_.collect{ - case v if v.valueType.unit.isDefined => v.label - }.mkString(" (", ", ", ")")) + if (stationTs.columns.nonEmpty) + stationTs.columns.collect{ + case v if v.valueType.unit.isDefined => v.label + }.mkString(" (", ", ", ")") + else "" else "" s"$spec$vars from $station$height" diff --git a/src/main/scala/se/lu/nateko/cp/meta/services/metaexport/SchemaOrg.scala b/src/main/scala/se/lu/nateko/cp/meta/services/metaexport/SchemaOrg.scala index 6f6811576..c7850dcb0 100644 --- a/src/main/scala/se/lu/nateko/cp/meta/services/metaexport/SchemaOrg.scala +++ b/src/main/scala/se/lu/nateko/cp/meta/services/metaexport/SchemaOrg.scala @@ -183,7 +183,7 @@ class SchemaOrg(handleProxies: HandleProxiesConfig)(using envri: Envri, envriCon ) } - val variableMeasured = asOptArray(dobj.specificInfo.fold(_.variables, _.columns))( + val variableMeasured = asOptArray(dobj.specificInfo.fold((_.variables), _.columns))( variable => JsObject( "@type" -> JsString("PropertyValue"), "name" -> JsString(variable.label), diff --git a/src/main/scala/se/lu/nateko/cp/meta/services/upload/DobjMetaReader.scala b/src/main/scala/se/lu/nateko/cp/meta/services/upload/DobjMetaReader.scala index 0ef04e06e..d8b9b4bda 100644 --- a/src/main/scala/se/lu/nateko/cp/meta/services/upload/DobjMetaReader.scala +++ b/src/main/scala/se/lu/nateko/cp/meta/services/upload/DobjMetaReader.scala @@ -272,7 +272,7 @@ trait DobjMetaReader(val vocab: CpVocab) extends CpmetaReader: case Some(interval) => addInstrDeplInfo(stationUri, interval, columns) columnsOptV.sinkOption.map: columnsOpt => - StationTimeSeriesMeta(acq, prod, nRows, lblCoverage, columnsOpt) + StationTimeSeriesMeta(acq, prod, nRows, lblCoverage, columnsOpt.getOrElse(Nil)) resV.flatMap(identity) end getStationTimeSerMeta @@ -325,7 +325,7 @@ trait DobjMetaReader(val vocab: CpVocab) extends CpmetaReader: station = station, samplingHeight = samplingHeightOpt.flatten, productionInfo = prod, - variables = Some(variables.flatten).filterNot(_.isEmpty) + variables = variables.flatten() ) def getContributors(objIri: IRI, contribPredicate: IRI)(using conn: DobjConn | DocConn): Validated[IndexedSeq[Agent]] = diff --git a/src/main/scala/se/lu/nateko/cp/meta/services/upload/StatementsProducer.scala b/src/main/scala/se/lu/nateko/cp/meta/services/upload/StatementsProducer.scala index ecec21ca0..f00a40f89 100644 --- a/src/main/scala/se/lu/nateko/cp/meta/services/upload/StatementsProducer.scala +++ b/src/main/scala/se/lu/nateko/cp/meta/services/upload/StatementsProducer.scala @@ -201,7 +201,7 @@ class StatementsProducer(vocab: CpVocab, metaVocab: CpmetaVocab) { makeSt(acq, metaVocab.prov.wasAssociatedWith, meta.forStation.map(_.toRdf)) ++ makeSt(acq, metaVocab.hasSamplingHeight, meta.samplingHeight.map(vocab.lit)) ++ makeSt(objUri, RDFS.SEEALSO, meta.customLandingPage.map(_.toRdf)) ++ - meta.variables.toSeq.flatten.flatMap(getL3VarInfoStatements(objUri, hash, _)) + meta.variables.flatMap(getL3VarInfoStatements(objUri, hash, _)) } private def getStationDataStatements(hash: Sha256Sum, meta: StationTimeSeriesDto)(using Envri): Seq[Statement] = { diff --git a/src/main/scala/se/lu/nateko/cp/meta/services/upload/validation/UploadValidator.scala b/src/main/scala/se/lu/nateko/cp/meta/services/upload/validation/UploadValidator.scala index b6594bb58..060273216 100644 --- a/src/main/scala/se/lu/nateko/cp/meta/services/upload/validation/UploadValidator.scala +++ b/src/main/scala/se/lu/nateko/cp/meta/services/upload/validation/UploadValidator.scala @@ -338,14 +338,16 @@ class UploadValidator(servers: DataObjectInstanceServers): if spec.specificDatasetType != DatasetType.SpatioTemporal then errors += "Wrong type of dataset for this object spec (must be spatiotemporal)" else - for vars <- spTempMeta.variables do spec.datasetSpec.fold( - errors += s"Data object specification ${spec.self.uri} lacks a dataset specification; cannot accept variable info." - ): dsSpec => - val valTypeLookupV = metaReader.getValTypeLookup(dsSpec.self.uri.toRdf) - errors ++= valTypeLookupV.errors - for valTypeLookup <- valTypeLookupV; varName <- vars do - if valTypeLookup.lookup(varName).isEmpty then errors += - s"Variable name '$varName' is not compatible with dataset specification ${dsSpec.self.uri}" + if (spTempMeta.variables.nonEmpty) { + spec.datasetSpec.fold( + errors += s"Data object specification ${spec.self.uri} lacks a dataset specification; cannot accept variable info." + ): dsSpec => + val valTypeLookupV = metaReader.getValTypeLookup(dsSpec.self.uri.toRdf) + errors ++= valTypeLookupV.errors + for valTypeLookup <- valTypeLookupV; varName <- spTempMeta.variables do + if valTypeLookup.lookup(varName).isEmpty then errors += + s"Variable name '$varName' is not compatible with dataset specification ${dsSpec.self.uri}" + } case Right(stationMeta) => if spec.specificDatasetType != DatasetType.StationTimeSeries diff --git a/src/main/twirl/views/DataLandingPage.scala.html b/src/main/twirl/views/DataLandingPage.scala.html index 1934cb3c4..d0a42f5c9 100644 --- a/src/main/twirl/views/DataLandingPage.scala.html +++ b/src/main/twirl/views/DataLandingPage.scala.html @@ -133,7 +133,7 @@

Production


- @for(variables <- varMetas){ + @if(varMetas.nonEmpty){

Previewable variables

@@ -155,7 +155,7 @@

Previewable variables

- @for(varInfo <- variables){ + @for(varInfo <- varMetas){ @@ -402,7 +402,7 @@

Technical information

} @instrumentDeploymentsPresent = @{ - varMetas.exists(_.exists(_.instrumentDeployments.isDefined)) + varMetas.exists(_.instrumentDeployments.isDefined) } @moratoriumText = @{ diff --git a/src/test/scala/se/lu/nateko/cp/meta/upload/L3UpdateWorkbench.scala b/src/test/scala/se/lu/nateko/cp/meta/upload/L3UpdateWorkbench.scala index 852aef671..6e80638c2 100644 --- a/src/test/scala/se/lu/nateko/cp/meta/upload/L3UpdateWorkbench.scala +++ b/src/test/scala/se/lu/nateko/cp/meta/upload/L3UpdateWorkbench.scala @@ -33,7 +33,7 @@ object L3UpdateWorkbench extends CpmetaJsonProtocol{ val l3 = dto.specificInfo.left.getOrElse(???) val l3updated = l3.copy( spatial = new URI("http://meta.icos-cp.eu/resources/latlonboxes/globalLatLonBox"), - variables = Some(Seq("emission")), + variables = Seq("emission"), ) dto.copy( submitterId = "CP", diff --git a/uploadgui/src/main/scala/se/lu/nateko/cp/meta/upload/Backend.scala b/uploadgui/src/main/scala/se/lu/nateko/cp/meta/upload/Backend.scala index 523de560f..0742c2506 100644 --- a/uploadgui/src/main/scala/se/lu/nateko/cp/meta/upload/Backend.scala +++ b/uploadgui/src/main/scala/se/lu/nateko/cp/meta/upload/Backend.scala @@ -96,18 +96,18 @@ object Backend { sparqlSelect(datasetVariableQuery(dataset)).map(_.map(toDatasetVar)) def tryIngestion( - file: File, spec: ObjSpec, nRows: Option[Int], varnames: Option[Seq[String]] + file: File, spec: ObjSpec, nRows: Option[Int], varnames: Seq[String] )(implicit envriConfig: EnvriConfig): Future[Unit] = { - val hasVars: Boolean = varnames.flatMap(_.headOption).isDefined + val hasVars = varnames.nonEmpty if (spec.dataset.isDefined && (spec.isStationTimeSer || hasVars)) || spec.isZip || spec.isNetCDF then val nRowsQ = nRows.fold("")(nr => s"&nRows=$nr") - val varsQ = varnames.fold(""){vns => - val varsJson = encodeURIComponent(Json.toJson(vns).toString) + val varsQ = if (varnames.nonEmpty) { + val varsJson = encodeURIComponent(Json.toJson(varnames).toString) s"&varnames=$varsJson" - } + } else { "" } val url = s"https://${envriConfig.dataHost}/tryingest?specUri=${spec.uri}$nRowsQ$varsQ" fetchOk("validate data object", url, new RequestInit{ diff --git a/uploadgui/src/main/scala/se/lu/nateko/cp/meta/upload/formcomponents/L3VarInfoForm.scala b/uploadgui/src/main/scala/se/lu/nateko/cp/meta/upload/formcomponents/L3VarInfoForm.scala index f5a2a0626..5ef1f214d 100644 --- a/uploadgui/src/main/scala/se/lu/nateko/cp/meta/upload/formcomponents/L3VarInfoForm.scala +++ b/uploadgui/src/main/scala/se/lu/nateko/cp/meta/upload/formcomponents/L3VarInfoForm.scala @@ -10,19 +10,17 @@ class L3VarInfoForm(elemId: String, notifyUpdate: () => Unit) { var list: IndexedSeq[DatasetVar] = IndexedSeq.empty - def values: Try[Option[Seq[DatasetVar]]] = if(elems.isEmpty) Success(None) else Try{ - Some(elems.map(_.varInfo.get).toIndexedSeq) + def values: Try[Seq[DatasetVar]] = if(elems.isEmpty) Success(Nil) else Try{ + elems.map(_.varInfo.get).toIndexedSeq } - def setValues(vars: Option[Seq[DatasetVar]]): Unit = { + def setValues(vars: Seq[DatasetVar]): Unit = { elems.foreach(_.remove()) elems.clear() - vars.foreach{vdtos => - vdtos.foreach{vdto => - val input = new L3VarInfoInput - input.setValue(vdto) - elems.append(input) - } + vars.foreach{vdto => + val input = new L3VarInfoInput + input.setValue(vdto) + elems.append(input) } } diff --git a/uploadgui/src/main/scala/se/lu/nateko/cp/meta/upload/subforms/SpatioTemporalPanel.scala b/uploadgui/src/main/scala/se/lu/nateko/cp/meta/upload/subforms/SpatioTemporalPanel.scala index 147de154b..25eb6787b 100644 --- a/uploadgui/src/main/scala/se/lu/nateko/cp/meta/upload/subforms/SpatioTemporalPanel.scala +++ b/uploadgui/src/main/scala/se/lu/nateko/cp/meta/upload/subforms/SpatioTemporalPanel.scala @@ -38,10 +38,10 @@ class SpatioTemporalPanel(covs: IndexedSeq[SpatialCoverage])(implicit bus: PubSu samplingHeight = height, production = prod, customLandingPage = customLanding, - variables = varInfo.map(_.map(_.uri.toString.split('/').last)) + variables = varInfo.map(_.uri.toString.split('/').last) ) - def varnames: Try[Option[Seq[String]]] = varInfoForm.values.map(_.map(_.map(_.title))) + def varnames: Try[Seq[String]] = varInfoForm.values.map(_.map(_.title)) private val titleInput = new TextInput("l3title", notifyUpdate, "elaborated product title") private val descriptionInput = new DescriptionInput("l3descr", notifyUpdate) @@ -55,14 +55,14 @@ class SpatioTemporalPanel(covs: IndexedSeq[SpatialCoverage])(implicit bus: PubSu private val varInfoForm = new L3VarInfoForm("l3varinfo-form", notifyUpdate) private val externalPageInput = new UriOptInput("l3landingpage", notifyUpdate) private var datasetSpec: Option[URI] = None - private var selsectedVars: Option[Seq[String]] = None + private var selsectedVars: Seq[String] = Nil def resetForm(): Unit = { Iterable( titleInput, descriptionInput, timeStartInput, timeStopInput, temporalResInput, externalPageInput ).foreach(_.reset()) - varInfoForm.setValues(None) + varInfoForm.setValues(Nil) spatialCovSelect.resetForm() } @@ -76,9 +76,8 @@ class SpatioTemporalPanel(covs: IndexedSeq[SpatialCoverage])(implicit bus: PubSu case GotStationsList(stations) => stationSelect.setOptions(stations) case GotVariableList(variables) => varInfoForm.list = variables - selsectedVars.map { variables => - varInfoForm.setValues(Some(variables.flatMap(uri => varInfoForm.list.find(_.uri.toString.split('/').last == uri)))) - } + varInfoForm.setValues(selsectedVars.flatMap(uri => + varInfoForm.list.find(_.uri.toString.split('/').last == uri))) } @@ -98,14 +97,19 @@ class SpatioTemporalPanel(covs: IndexedSeq[SpatialCoverage])(implicit bus: PubSu samplingHeightInput.value = spatTemp.samplingHeight externalPageInput.value = spatTemp.customLandingPage selsectedVars = spatTemp.variables - spatTemp.variables.map { varUris => - datasetSpec.map { dataset => - whenDone(getVariables(dataset)) { variables => - varInfoForm.list = variables - varInfoForm.setValues(Some(varUris.flatMap(uri => varInfoForm.list.find(_.uri.toString.split('/').last == uri)))) + if (selsectedVars.nonEmpty) { + datasetSpec match { + case None => () + case Some(dataset) => { + whenDone(getVariables(dataset)) { variables => + varInfoForm.list = variables + varInfoForm.setValues(selsectedVars.flatMap(uri => + varInfoForm.list.find(_.uri.toString.split('/').last == uri) + )) + } + } } } - } spatialCovSelect.handleReceivedSpatialCoverage(Some(spatTemp.spatial)) show() case _ =>
@(varInfo.label) @(varInfo.valueType.self.label.getOrElse(""))