2021-12-06 14:24:03 +01:00
|
|
|
package eu.dnetlib.dhp.doiboost.crossref
|
2020-04-29 13:13:02 +02:00
|
|
|
|
2023-02-15 10:29:13 +01:00
|
|
|
import eu.dnetlib.dhp.schema.common.ModelConstants
|
2020-05-13 10:38:04 +02:00
|
|
|
import eu.dnetlib.dhp.schema.oaf._
|
2020-05-11 09:38:27 +02:00
|
|
|
import eu.dnetlib.dhp.utils.DHPUtils
|
2021-12-06 14:24:03 +01:00
|
|
|
import eu.dnetlib.doiboost.crossref.Crossref2Oaf
|
2020-05-22 15:15:09 +02:00
|
|
|
import org.codehaus.jackson.map.{ObjectMapper, SerializationConfig}
|
2023-02-15 10:29:13 +01:00
|
|
|
import org.json4s
|
|
|
|
import org.json4s.JsonAST.{JField, JObject, JString}
|
|
|
|
import org.json4s.{DefaultFormats, JValue}
|
|
|
|
import org.json4s.jackson.JsonMethods
|
2020-04-29 13:13:02 +02:00
|
|
|
import org.junit.jupiter.api.Assertions._
|
2020-05-13 10:38:04 +02:00
|
|
|
import org.junit.jupiter.api.Test
|
2020-04-29 13:13:02 +02:00
|
|
|
import org.slf4j.{Logger, LoggerFactory}
|
|
|
|
|
|
|
|
import scala.collection.JavaConverters._
|
2020-05-13 10:38:04 +02:00
|
|
|
import scala.io.Source
|
2020-05-11 09:38:27 +02:00
|
|
|
import scala.util.matching.Regex
|
2020-04-29 13:13:02 +02:00
|
|
|
|
|
|
|
class CrossrefMappingTest {
|
|
|
|
|
|
|
|
val logger: Logger = LoggerFactory.getLogger(Crossref2Oaf.getClass)
|
|
|
|
val mapper = new ObjectMapper()
|
|
|
|
|
|
|
|
@Test
|
|
|
|
def testFunderRelationshipsMapping(): Unit = {
|
2022-01-11 16:57:48 +01:00
|
|
|
val template = Source
|
|
|
|
.fromInputStream(
|
|
|
|
getClass.getResourceAsStream("/eu/dnetlib/doiboost/crossref/article_funder_template.json")
|
|
|
|
)
|
|
|
|
.mkString
|
|
|
|
val funder_doi = Source
|
|
|
|
.fromInputStream(getClass.getResourceAsStream("/eu/dnetlib/doiboost/crossref/funder_doi"))
|
|
|
|
.mkString
|
|
|
|
val funder_name = Source
|
|
|
|
.fromInputStream(getClass.getResourceAsStream("/eu/dnetlib/doiboost/crossref/funder_doi"))
|
|
|
|
.mkString
|
2020-04-29 13:13:02 +02:00
|
|
|
|
2023-02-13 16:25:47 +01:00
|
|
|
for (line <- funder_doi.linesWithSeparators.map(l => l.stripLineEnd)) {
|
2020-04-29 13:13:02 +02:00
|
|
|
val json = template.replace("%s", line)
|
|
|
|
val resultList: List[Oaf] = Crossref2Oaf.convert(json)
|
|
|
|
assertTrue(resultList.nonEmpty)
|
|
|
|
checkRelation(resultList)
|
|
|
|
}
|
2023-02-13 16:25:47 +01:00
|
|
|
for (line <- funder_name.linesWithSeparators.map(l => l.stripLineEnd)) {
|
2020-04-29 13:13:02 +02:00
|
|
|
val json = template.replace("%s", line)
|
|
|
|
val resultList: List[Oaf] = Crossref2Oaf.convert(json)
|
|
|
|
assertTrue(resultList.nonEmpty)
|
|
|
|
checkRelation(resultList)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
def checkRelation(generatedOAF: List[Oaf]): Unit = {
|
|
|
|
|
2022-01-11 16:57:48 +01:00
|
|
|
val rels: List[Relation] =
|
|
|
|
generatedOAF.filter(p => p.isInstanceOf[Relation]).asInstanceOf[List[Relation]]
|
2020-04-29 13:13:02 +02:00
|
|
|
assertFalse(rels.isEmpty)
|
|
|
|
rels.foreach(relation => {
|
|
|
|
val relJson = mapper.writeValueAsString(relation)
|
|
|
|
|
|
|
|
assertNotNull(relation.getSource, s"Source of relation null $relJson")
|
|
|
|
assertNotNull(relation.getTarget, s"Target of relation null $relJson")
|
|
|
|
assertFalse(relation.getTarget.isEmpty, s"Target is empty: $relJson")
|
|
|
|
assertFalse(relation.getRelClass.isEmpty, s"RelClass is empty: $relJson")
|
|
|
|
assertFalse(relation.getRelType.isEmpty, s"RelType is empty: $relJson")
|
|
|
|
assertFalse(relation.getSubRelType.isEmpty, s"SubRelType is empty: $relJson")
|
|
|
|
|
|
|
|
})
|
|
|
|
|
2020-05-22 15:15:09 +02:00
|
|
|
}
|
|
|
|
|
2021-03-26 13:56:29 +01:00
|
|
|
@Test
|
2022-01-11 16:57:48 +01:00
|
|
|
def testSum(): Unit = {
|
|
|
|
val from: Long = 1613135645000L
|
|
|
|
val delta: Long = 1000000L
|
2021-03-26 13:56:29 +01:00
|
|
|
|
2022-01-11 16:57:48 +01:00
|
|
|
println(s"updating from value: $from -> ${from + delta}")
|
2021-03-26 13:56:29 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-04-26 09:56:27 +02:00
|
|
|
@Test
|
|
|
|
def crossrefIssueDateTest(): Unit = {
|
2022-05-03 13:37:15 +02:00
|
|
|
val json =
|
|
|
|
Source.fromInputStream(getClass.getResourceAsStream("/eu/dnetlib/doiboost/crossref/issue_date.json")).mkString
|
2022-04-26 09:56:27 +02:00
|
|
|
assertNotNull(json)
|
|
|
|
assertFalse(json.isEmpty)
|
|
|
|
val resultList: List[Oaf] = Crossref2Oaf.convert(json)
|
|
|
|
assertTrue(resultList.nonEmpty)
|
|
|
|
|
|
|
|
val items = resultList.filter(p => p.isInstanceOf[Result])
|
|
|
|
|
|
|
|
println(mapper.writeValueAsString(items.head))
|
|
|
|
}
|
|
|
|
|
2020-06-25 10:48:15 +02:00
|
|
|
@Test
|
2022-01-11 16:57:48 +01:00
|
|
|
def testOrcidID(): Unit = {
|
|
|
|
val json = Source
|
|
|
|
.fromInputStream(
|
|
|
|
getClass.getResourceAsStream("/eu/dnetlib/doiboost/crossref/orcid_data.json")
|
|
|
|
)
|
|
|
|
.mkString
|
2020-06-25 10:48:15 +02:00
|
|
|
|
|
|
|
assertNotNull(json)
|
2022-04-26 09:56:27 +02:00
|
|
|
assertFalse(json.isEmpty)
|
2020-06-25 10:48:15 +02:00
|
|
|
|
|
|
|
val resultList: List[Oaf] = Crossref2Oaf.convert(json)
|
|
|
|
|
|
|
|
assertTrue(resultList.nonEmpty)
|
|
|
|
|
|
|
|
val items = resultList.filter(p => p.isInstanceOf[Result])
|
|
|
|
|
|
|
|
mapper.getSerializationConfig.enable(SerializationConfig.Feature.INDENT_OUTPUT)
|
|
|
|
items.foreach(p => println(mapper.writeValueAsString(p)))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-02-20 14:45:27 +01:00
|
|
|
private def parseJson(input: String): JValue = {
|
2023-02-15 10:29:13 +01:00
|
|
|
implicit lazy val formats: DefaultFormats.type = org.json4s.DefaultFormats
|
|
|
|
lazy val json: json4s.JValue = JsonMethods.parse(input)
|
|
|
|
|
|
|
|
json
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
2023-02-20 14:45:27 +01:00
|
|
|
def testCitationRelations(): Unit = {
|
|
|
|
val json = Source
|
|
|
|
.fromInputStream(getClass.getResourceAsStream("/eu/dnetlib/doiboost/crossref/publication_license_embargo.json"))
|
|
|
|
.mkString
|
2023-02-15 10:29:13 +01:00
|
|
|
|
|
|
|
assertNotNull(json)
|
|
|
|
assertFalse(json.isEmpty)
|
|
|
|
|
2023-02-20 14:45:27 +01:00
|
|
|
val result: List[Oaf] = Crossref2Oaf.convert(json)
|
2023-02-15 10:29:13 +01:00
|
|
|
|
|
|
|
assertTrue(result.nonEmpty)
|
|
|
|
|
|
|
|
val j = parseJson(json)
|
|
|
|
|
|
|
|
val doisReference: List[String] = for {
|
2023-02-20 14:45:27 +01:00
|
|
|
JObject(reference_json) <- j \ "reference"
|
2023-02-15 10:29:13 +01:00
|
|
|
JField("DOI", JString(doi_json)) <- reference_json
|
|
|
|
} yield doi_json
|
|
|
|
|
2023-02-20 14:45:27 +01:00
|
|
|
val relationList: List[Relation] = result
|
|
|
|
.filter(s => s.isInstanceOf[Relation])
|
|
|
|
.map(r => r.asInstanceOf[Relation])
|
2023-05-03 14:03:01 +02:00
|
|
|
.filter(r => r.getSubRelType.equalsIgnoreCase(Relation.SUBRELTYPE.citation))
|
2023-02-15 10:29:13 +01:00
|
|
|
|
|
|
|
assertNotNull(relationList)
|
|
|
|
assertFalse(relationList.isEmpty)
|
|
|
|
|
2023-02-20 14:45:27 +01:00
|
|
|
assertEquals(doisReference.size * 2, relationList.size)
|
|
|
|
|
|
|
|
mapper.getSerializationConfig.enable(SerializationConfig.Feature.INDENT_OUTPUT)
|
|
|
|
relationList.foreach(p => println(mapper.writeValueAsString(p)))
|
2023-02-15 10:29:13 +01:00
|
|
|
}
|
|
|
|
|
2020-05-28 09:57:46 +02:00
|
|
|
@Test
|
2022-01-11 16:57:48 +01:00
|
|
|
def testEmptyTitle(): Unit = {
|
|
|
|
val json = Source
|
|
|
|
.fromInputStream(
|
|
|
|
getClass.getResourceAsStream("/eu/dnetlib/doiboost/crossref/empty_title.json")
|
|
|
|
)
|
|
|
|
.mkString
|
2020-05-28 09:57:46 +02:00
|
|
|
|
|
|
|
assertNotNull(json)
|
|
|
|
assertFalse(json.isEmpty);
|
|
|
|
|
|
|
|
val resultList: List[Oaf] = Crossref2Oaf.convert(json)
|
|
|
|
|
|
|
|
assertTrue(resultList.nonEmpty)
|
|
|
|
|
|
|
|
val items = resultList.filter(p => p.isInstanceOf[Result])
|
|
|
|
|
|
|
|
mapper.getSerializationConfig.enable(SerializationConfig.Feature.INDENT_OUTPUT)
|
|
|
|
items.foreach(p => println(mapper.writeValueAsString(p)))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-05-22 15:15:09 +02:00
|
|
|
@Test
|
|
|
|
def testPeerReviewed(): Unit = {
|
2022-01-11 16:57:48 +01:00
|
|
|
val json = Source
|
|
|
|
.fromInputStream(getClass.getResourceAsStream("/eu/dnetlib/doiboost/crossref/prwTest.json"))
|
|
|
|
.mkString
|
2020-05-22 15:15:09 +02:00
|
|
|
mapper.getSerializationConfig.enable(SerializationConfig.Feature.INDENT_OUTPUT)
|
|
|
|
|
|
|
|
assertNotNull(json)
|
|
|
|
assertFalse(json.isEmpty);
|
|
|
|
|
|
|
|
val resultList: List[Oaf] = Crossref2Oaf.convert(json)
|
|
|
|
|
|
|
|
assertTrue(resultList.nonEmpty)
|
|
|
|
|
|
|
|
val items = resultList.filter(p => p.isInstanceOf[Result])
|
|
|
|
|
|
|
|
items.foreach(p => logger.info(mapper.writeValueAsString(p)))
|
|
|
|
|
2020-05-11 09:38:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
def extractECAward(award: String): String = {
|
|
|
|
val awardECRegex: Regex = "[0-9]{4,9}".r
|
|
|
|
if (awardECRegex.findAllIn(award).hasNext)
|
|
|
|
return awardECRegex.findAllIn(award).max
|
|
|
|
null
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
def extractECTest(): Unit = {
|
2022-01-11 16:57:48 +01:00
|
|
|
val s = "FP7/2007-2013"
|
2020-05-11 09:38:27 +02:00
|
|
|
val awardExtracted = extractECAward(s)
|
|
|
|
println(awardExtracted)
|
|
|
|
|
|
|
|
println(DHPUtils.md5(awardExtracted))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
def testJournalRelation(): Unit = {
|
2022-01-11 16:57:48 +01:00
|
|
|
val json = Source
|
|
|
|
.fromInputStream(getClass.getResourceAsStream("/eu/dnetlib/doiboost/crossref/awardTest.json"))
|
|
|
|
.mkString
|
2020-05-11 09:38:27 +02:00
|
|
|
assertNotNull(json)
|
|
|
|
|
|
|
|
assertFalse(json.isEmpty)
|
|
|
|
|
|
|
|
val resultList: List[Oaf] = Crossref2Oaf.convert(json)
|
|
|
|
|
|
|
|
assertTrue(resultList.nonEmpty)
|
2022-01-11 16:57:48 +01:00
|
|
|
val rels: List[Relation] =
|
|
|
|
resultList.filter(p => p.isInstanceOf[Relation]).map(r => r.asInstanceOf[Relation])
|
2020-05-11 09:38:27 +02:00
|
|
|
|
2020-05-20 17:05:46 +02:00
|
|
|
rels.foreach(s => logger.info(s.getTarget))
|
2022-01-11 16:57:48 +01:00
|
|
|
assertEquals(rels.size, 6)
|
2020-05-11 09:38:27 +02:00
|
|
|
|
2020-04-29 13:13:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
def testConvertBookFromCrossRef2Oaf(): Unit = {
|
2022-01-11 16:57:48 +01:00
|
|
|
val json = Source
|
|
|
|
.fromInputStream(getClass.getResourceAsStream("/eu/dnetlib/doiboost/crossref/book.json"))
|
|
|
|
.mkString
|
2020-04-29 13:13:02 +02:00
|
|
|
assertNotNull(json)
|
|
|
|
|
|
|
|
assertFalse(json.isEmpty);
|
|
|
|
|
|
|
|
val resultList: List[Oaf] = Crossref2Oaf.convert(json)
|
|
|
|
|
|
|
|
assertTrue(resultList.nonEmpty)
|
|
|
|
|
|
|
|
val items = resultList.filter(p => p.isInstanceOf[Result])
|
|
|
|
|
|
|
|
assert(items.nonEmpty)
|
|
|
|
assert(items.size == 1)
|
|
|
|
val result: Result = items.head.asInstanceOf[Result]
|
|
|
|
assertNotNull(result)
|
|
|
|
|
|
|
|
logger.info(mapper.writeValueAsString(result));
|
|
|
|
|
|
|
|
assertNotNull(result.getDataInfo, "Datainfo test not null Failed");
|
|
|
|
assertNotNull(
|
|
|
|
result.getDataInfo.getProvenanceaction,
|
2022-01-11 16:57:48 +01:00
|
|
|
"DataInfo/Provenance test not null Failed"
|
|
|
|
);
|
2020-04-29 13:13:02 +02:00
|
|
|
assertFalse(
|
|
|
|
result.getDataInfo.getProvenanceaction.getClassid.isEmpty,
|
2022-01-11 16:57:48 +01:00
|
|
|
"DataInfo/Provenance/classId test not null Failed"
|
|
|
|
);
|
2020-04-29 13:13:02 +02:00
|
|
|
assertFalse(
|
|
|
|
result.getDataInfo.getProvenanceaction.getClassname.isEmpty,
|
2022-01-11 16:57:48 +01:00
|
|
|
"DataInfo/Provenance/className test not null Failed"
|
|
|
|
);
|
2020-04-29 13:13:02 +02:00
|
|
|
assertFalse(
|
|
|
|
result.getDataInfo.getProvenanceaction.getSchemeid.isEmpty,
|
2022-01-11 16:57:48 +01:00
|
|
|
"DataInfo/Provenance/SchemeId test not null Failed"
|
|
|
|
);
|
2020-04-29 13:13:02 +02:00
|
|
|
|
|
|
|
assertNotNull(result.getCollectedfrom, "CollectedFrom test not null Failed");
|
|
|
|
assertFalse(result.getCollectedfrom.isEmpty);
|
|
|
|
|
|
|
|
val collectedFromList = result.getCollectedfrom.asScala
|
2022-01-11 16:57:48 +01:00
|
|
|
assert(
|
2022-01-12 09:40:28 +01:00
|
|
|
collectedFromList.exists(c => c.getKey.equalsIgnoreCase("10|openaire____::081b82f96300b6a6e3d282bad31cb6e2")),
|
2022-01-11 16:57:48 +01:00
|
|
|
"Wrong collected from assertion"
|
|
|
|
)
|
|
|
|
|
|
|
|
assert(
|
|
|
|
collectedFromList.exists(c => c.getValue.equalsIgnoreCase("crossref")),
|
|
|
|
"Wrong collected from assertion"
|
|
|
|
)
|
2020-04-29 13:13:02 +02:00
|
|
|
|
|
|
|
val relevantDates = result.getRelevantdate.asScala
|
|
|
|
|
2022-01-11 16:57:48 +01:00
|
|
|
assert(
|
|
|
|
relevantDates.exists(d => d.getQualifier.getClassid.equalsIgnoreCase("created")),
|
|
|
|
"Missing relevant date of type created"
|
|
|
|
)
|
|
|
|
assert(
|
|
|
|
relevantDates.exists(d => d.getQualifier.getClassid.equalsIgnoreCase("published-online")),
|
|
|
|
"Missing relevant date of type published-online"
|
|
|
|
)
|
|
|
|
assert(
|
|
|
|
relevantDates.exists(d => d.getQualifier.getClassid.equalsIgnoreCase("published-print")),
|
|
|
|
"Missing relevant date of type published-print"
|
|
|
|
)
|
2020-04-29 13:13:02 +02:00
|
|
|
val rels = resultList.filter(p => p.isInstanceOf[Relation])
|
|
|
|
assert(rels.isEmpty)
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
def testConvertPreprintFromCrossRef2Oaf(): Unit = {
|
2022-01-11 16:57:48 +01:00
|
|
|
val json = Source
|
|
|
|
.fromInputStream(getClass.getResourceAsStream("/eu/dnetlib/doiboost/crossref/preprint.json"))
|
|
|
|
.mkString
|
2020-04-29 13:13:02 +02:00
|
|
|
assertNotNull(json)
|
|
|
|
|
|
|
|
assertFalse(json.isEmpty);
|
|
|
|
|
|
|
|
val resultList: List[Oaf] = Crossref2Oaf.convert(json)
|
|
|
|
|
|
|
|
assertTrue(resultList.nonEmpty)
|
|
|
|
|
|
|
|
val items = resultList.filter(p => p.isInstanceOf[Publication])
|
|
|
|
|
|
|
|
assert(items.nonEmpty)
|
|
|
|
assert(items.size == 1)
|
|
|
|
val result: Result = items.head.asInstanceOf[Publication]
|
|
|
|
assertNotNull(result)
|
|
|
|
|
|
|
|
logger.info(mapper.writeValueAsString(result));
|
|
|
|
|
|
|
|
assertNotNull(result.getDataInfo, "Datainfo test not null Failed");
|
|
|
|
assertNotNull(
|
|
|
|
result.getDataInfo.getProvenanceaction,
|
2022-01-11 16:57:48 +01:00
|
|
|
"DataInfo/Provenance test not null Failed"
|
|
|
|
);
|
2020-04-29 13:13:02 +02:00
|
|
|
assertFalse(
|
|
|
|
result.getDataInfo.getProvenanceaction.getClassid.isEmpty,
|
2022-01-11 16:57:48 +01:00
|
|
|
"DataInfo/Provenance/classId test not null Failed"
|
|
|
|
);
|
2020-04-29 13:13:02 +02:00
|
|
|
assertFalse(
|
|
|
|
result.getDataInfo.getProvenanceaction.getClassname.isEmpty,
|
2022-01-11 16:57:48 +01:00
|
|
|
"DataInfo/Provenance/className test not null Failed"
|
|
|
|
);
|
2020-04-29 13:13:02 +02:00
|
|
|
assertFalse(
|
|
|
|
result.getDataInfo.getProvenanceaction.getSchemeid.isEmpty,
|
2022-01-11 16:57:48 +01:00
|
|
|
"DataInfo/Provenance/SchemeId test not null Failed"
|
|
|
|
);
|
2020-04-29 13:13:02 +02:00
|
|
|
|
|
|
|
assertNotNull(result.getCollectedfrom, "CollectedFrom test not null Failed");
|
|
|
|
assertFalse(result.getCollectedfrom.isEmpty);
|
|
|
|
|
|
|
|
val collectedFromList = result.getCollectedfrom.asScala
|
2022-01-11 16:57:48 +01:00
|
|
|
assert(
|
2022-01-12 09:40:28 +01:00
|
|
|
collectedFromList.exists(c => c.getKey.equalsIgnoreCase("10|openaire____::081b82f96300b6a6e3d282bad31cb6e2")),
|
2022-01-11 16:57:48 +01:00
|
|
|
"Wrong collected from assertion"
|
|
|
|
)
|
|
|
|
|
|
|
|
assert(
|
|
|
|
collectedFromList.exists(c => c.getValue.equalsIgnoreCase("crossref")),
|
|
|
|
"Wrong collected from assertion"
|
|
|
|
)
|
2020-04-29 13:13:02 +02:00
|
|
|
|
|
|
|
val relevantDates = result.getRelevantdate.asScala
|
|
|
|
|
2022-01-11 16:57:48 +01:00
|
|
|
assert(
|
|
|
|
relevantDates.exists(d => d.getQualifier.getClassid.equalsIgnoreCase("created")),
|
|
|
|
"Missing relevant date of type created"
|
|
|
|
)
|
|
|
|
assert(
|
|
|
|
relevantDates.exists(d => d.getQualifier.getClassid.equalsIgnoreCase("available")),
|
|
|
|
"Missing relevant date of type available"
|
|
|
|
)
|
|
|
|
assert(
|
|
|
|
relevantDates.exists(d => d.getQualifier.getClassid.equalsIgnoreCase("accepted")),
|
|
|
|
"Missing relevant date of type accepted"
|
|
|
|
)
|
|
|
|
assert(
|
|
|
|
relevantDates.exists(d => d.getQualifier.getClassid.equalsIgnoreCase("published-online")),
|
|
|
|
"Missing relevant date of type published-online"
|
|
|
|
)
|
|
|
|
assert(
|
|
|
|
relevantDates.exists(d => d.getQualifier.getClassid.equalsIgnoreCase("published-print")),
|
|
|
|
"Missing relevant date of type published-print"
|
|
|
|
)
|
2020-04-29 13:13:02 +02:00
|
|
|
val rels = resultList.filter(p => p.isInstanceOf[Relation])
|
|
|
|
assert(rels.isEmpty)
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
def testConvertDatasetFromCrossRef2Oaf(): Unit = {
|
2022-01-11 16:57:48 +01:00
|
|
|
val json = Source
|
|
|
|
.fromInputStream(getClass.getResourceAsStream("/eu/dnetlib/doiboost/crossref/dataset.json"))
|
|
|
|
.mkString
|
2020-04-29 13:13:02 +02:00
|
|
|
assertNotNull(json)
|
|
|
|
|
|
|
|
assertFalse(json.isEmpty);
|
|
|
|
|
|
|
|
val resultList: List[Oaf] = Crossref2Oaf.convert(json)
|
|
|
|
|
|
|
|
assertTrue(resultList.nonEmpty)
|
|
|
|
|
|
|
|
val items = resultList.filter(p => p.isInstanceOf[Dataset])
|
|
|
|
|
|
|
|
assert(items.nonEmpty)
|
|
|
|
assert(items.size == 1)
|
|
|
|
val result: Result = items.head.asInstanceOf[Dataset]
|
|
|
|
assertNotNull(result)
|
|
|
|
|
|
|
|
logger.info(mapper.writeValueAsString(result));
|
|
|
|
|
|
|
|
assertNotNull(result.getDataInfo, "Datainfo test not null Failed");
|
|
|
|
assertNotNull(
|
|
|
|
result.getDataInfo.getProvenanceaction,
|
2022-01-11 16:57:48 +01:00
|
|
|
"DataInfo/Provenance test not null Failed"
|
|
|
|
);
|
2020-04-29 13:13:02 +02:00
|
|
|
assertFalse(
|
|
|
|
result.getDataInfo.getProvenanceaction.getClassid.isEmpty,
|
2022-01-11 16:57:48 +01:00
|
|
|
"DataInfo/Provenance/classId test not null Failed"
|
|
|
|
);
|
2020-04-29 13:13:02 +02:00
|
|
|
assertFalse(
|
|
|
|
result.getDataInfo.getProvenanceaction.getClassname.isEmpty,
|
2022-01-11 16:57:48 +01:00
|
|
|
"DataInfo/Provenance/className test not null Failed"
|
|
|
|
);
|
2020-04-29 13:13:02 +02:00
|
|
|
assertFalse(
|
|
|
|
result.getDataInfo.getProvenanceaction.getSchemeid.isEmpty,
|
2022-01-11 16:57:48 +01:00
|
|
|
"DataInfo/Provenance/SchemeId test not null Failed"
|
|
|
|
);
|
2020-04-29 13:13:02 +02:00
|
|
|
|
|
|
|
assertNotNull(result.getCollectedfrom, "CollectedFrom test not null Failed");
|
|
|
|
assertFalse(result.getCollectedfrom.isEmpty);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
def testConvertArticleFromCrossRef2Oaf(): Unit = {
|
2022-01-11 16:57:48 +01:00
|
|
|
val json = Source
|
|
|
|
.fromInputStream(getClass.getResourceAsStream("/eu/dnetlib/doiboost/crossref/article.json"))
|
|
|
|
.mkString
|
2020-04-29 13:13:02 +02:00
|
|
|
assertNotNull(json)
|
|
|
|
|
|
|
|
assertFalse(json.isEmpty);
|
|
|
|
|
|
|
|
val resultList: List[Oaf] = Crossref2Oaf.convert(json)
|
|
|
|
|
|
|
|
assertTrue(resultList.nonEmpty)
|
|
|
|
|
|
|
|
val items = resultList.filter(p => p.isInstanceOf[Publication])
|
|
|
|
|
|
|
|
assert(items.nonEmpty)
|
|
|
|
assert(items.size == 1)
|
|
|
|
val result: Result = items.head.asInstanceOf[Publication]
|
|
|
|
assertNotNull(result)
|
|
|
|
|
|
|
|
logger.info(mapper.writeValueAsString(result));
|
|
|
|
|
|
|
|
assertNotNull(result.getDataInfo, "Datainfo test not null Failed");
|
|
|
|
assertNotNull(
|
|
|
|
result.getDataInfo.getProvenanceaction,
|
2022-01-11 16:57:48 +01:00
|
|
|
"DataInfo/Provenance test not null Failed"
|
|
|
|
);
|
2020-04-29 13:13:02 +02:00
|
|
|
assertFalse(
|
|
|
|
result.getDataInfo.getProvenanceaction.getClassid.isEmpty,
|
2022-01-11 16:57:48 +01:00
|
|
|
"DataInfo/Provenance/classId test not null Failed"
|
|
|
|
);
|
2020-04-29 13:13:02 +02:00
|
|
|
assertFalse(
|
|
|
|
result.getDataInfo.getProvenanceaction.getClassname.isEmpty,
|
2022-01-11 16:57:48 +01:00
|
|
|
"DataInfo/Provenance/className test not null Failed"
|
|
|
|
);
|
2020-04-29 13:13:02 +02:00
|
|
|
assertFalse(
|
|
|
|
result.getDataInfo.getProvenanceaction.getSchemeid.isEmpty,
|
2022-01-11 16:57:48 +01:00
|
|
|
"DataInfo/Provenance/SchemeId test not null Failed"
|
|
|
|
);
|
2020-04-29 13:13:02 +02:00
|
|
|
|
|
|
|
assertNotNull(result.getCollectedfrom, "CollectedFrom test not null Failed");
|
|
|
|
assertFalse(result.getCollectedfrom.isEmpty);
|
|
|
|
|
|
|
|
val collectedFromList = result.getCollectedfrom.asScala
|
2022-01-11 16:57:48 +01:00
|
|
|
assert(
|
2022-01-12 09:40:28 +01:00
|
|
|
collectedFromList.exists(c => c.getKey.equalsIgnoreCase("10|openaire____::081b82f96300b6a6e3d282bad31cb6e2")),
|
2022-01-11 16:57:48 +01:00
|
|
|
"Wrong collected from assertion"
|
|
|
|
)
|
|
|
|
|
|
|
|
assert(
|
|
|
|
collectedFromList.exists(c => c.getValue.equalsIgnoreCase("crossref")),
|
|
|
|
"Wrong collected from assertion"
|
|
|
|
)
|
2020-04-29 13:13:02 +02:00
|
|
|
|
|
|
|
val relevantDates = result.getRelevantdate.asScala
|
|
|
|
|
2022-01-11 16:57:48 +01:00
|
|
|
assert(
|
|
|
|
relevantDates.exists(d => d.getQualifier.getClassid.equalsIgnoreCase("created")),
|
|
|
|
"Missing relevant date of type created"
|
|
|
|
)
|
2020-04-29 13:13:02 +02:00
|
|
|
|
|
|
|
val rels = resultList.filter(p => p.isInstanceOf[Relation]).asInstanceOf[List[Relation]]
|
|
|
|
assertFalse(rels.isEmpty)
|
|
|
|
rels.foreach(relation => {
|
|
|
|
assertNotNull(relation)
|
|
|
|
assertFalse(relation.getSource.isEmpty)
|
|
|
|
assertFalse(relation.getTarget.isEmpty)
|
|
|
|
assertFalse(relation.getRelClass.isEmpty)
|
|
|
|
assertFalse(relation.getRelType.isEmpty)
|
|
|
|
assertFalse(relation.getSubRelType.isEmpty)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-06-08 14:55:19 +02:00
|
|
|
@Test
|
|
|
|
def testConvertFromCrossRef2OafIssue(): Unit = {
|
|
|
|
val json = Source
|
|
|
|
.fromInputStream(getClass.getResourceAsStream("/eu/dnetlib/doiboost/crossref/article_nojournal.json"))
|
|
|
|
.mkString
|
|
|
|
assertNotNull(json)
|
|
|
|
|
|
|
|
assertFalse(json.isEmpty);
|
|
|
|
|
|
|
|
val resultList: List[Oaf] = Crossref2Oaf.convert(json)
|
|
|
|
|
|
|
|
assertTrue(resultList.nonEmpty)
|
|
|
|
|
|
|
|
val items = resultList.filter(p => p.isInstanceOf[Publication])
|
|
|
|
|
|
|
|
assert(items.nonEmpty)
|
|
|
|
assert(items.size == 1)
|
|
|
|
val pub: Publication = items.head.asInstanceOf[Publication]
|
|
|
|
|
|
|
|
assertNotNull(pub.getJournal.getIssnPrinted)
|
|
|
|
assertNotNull(pub.getJournal.getIssnOnline)
|
|
|
|
assertNotNull(pub.getJournal.getName)
|
|
|
|
|
2020-04-29 13:13:02 +02:00
|
|
|
}
|
|
|
|
|
2021-06-18 09:43:32 +02:00
|
|
|
@Test
|
|
|
|
def testSetDateOfAcceptanceCrossRef2Oaf(): Unit = {
|
|
|
|
|
2022-01-11 16:57:48 +01:00
|
|
|
val json = Source
|
|
|
|
.fromInputStream(getClass.getResourceAsStream("/eu/dnetlib/doiboost/crossref/dump_file.json"))
|
|
|
|
.mkString
|
2021-06-18 09:43:32 +02:00
|
|
|
assertNotNull(json)
|
|
|
|
|
|
|
|
assertFalse(json.isEmpty);
|
|
|
|
|
|
|
|
val resultList: List[Oaf] = Crossref2Oaf.convert(json)
|
|
|
|
|
|
|
|
assertTrue(resultList.nonEmpty)
|
|
|
|
|
|
|
|
val items = resultList.filter(p => p.isInstanceOf[Publication])
|
|
|
|
|
|
|
|
assert(items.nonEmpty)
|
|
|
|
assert(items.size == 1)
|
|
|
|
val result: Result = items.head.asInstanceOf[Publication]
|
|
|
|
assertNotNull(result)
|
|
|
|
logger.info(mapper.writeValueAsString(result));
|
|
|
|
}
|
|
|
|
|
2021-06-30 12:55:37 +02:00
|
|
|
@Test
|
|
|
|
def testNormalizeDOI(): Unit = {
|
2022-01-11 16:57:48 +01:00
|
|
|
val template = Source
|
|
|
|
.fromInputStream(
|
|
|
|
getClass.getResourceAsStream("/eu/dnetlib/doiboost/crossref/article_funder_template.json")
|
|
|
|
)
|
|
|
|
.mkString
|
|
|
|
val line: String =
|
|
|
|
"\"funder\": [{\"name\": \"Wellcome Trust Masters Fellowship\",\"award\": [\"090633\"]}],"
|
2021-06-30 12:55:37 +02:00
|
|
|
val json = template.replace("%s", line)
|
|
|
|
val resultList: List[Oaf] = Crossref2Oaf.convert(json)
|
|
|
|
assertTrue(resultList.nonEmpty)
|
|
|
|
val items = resultList.filter(p => p.isInstanceOf[Publication])
|
|
|
|
val result: Result = items.head.asInstanceOf[Publication]
|
|
|
|
|
|
|
|
result.getPid.asScala.foreach(pid => assertTrue(pid.getQualifier.getClassid.equals("doi")))
|
|
|
|
assertTrue(result.getPid.size() == 1)
|
2022-01-11 16:57:48 +01:00
|
|
|
result.getPid.asScala.foreach(pid =>
|
|
|
|
assertTrue(pid.getValue.equals("10.26850/1678-4618EQJ.v35.1.2010.p41-46".toLowerCase()))
|
|
|
|
)
|
2021-06-30 12:55:37 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
def testNormalizeDOI2(): Unit = {
|
2022-01-11 16:57:48 +01:00
|
|
|
val template = Source
|
|
|
|
.fromInputStream(getClass.getResourceAsStream("/eu/dnetlib/doiboost/crossref/article.json"))
|
|
|
|
.mkString
|
2021-06-30 12:55:37 +02:00
|
|
|
|
|
|
|
val resultList: List[Oaf] = Crossref2Oaf.convert(template)
|
|
|
|
assertTrue(resultList.nonEmpty)
|
|
|
|
val items = resultList.filter(p => p.isInstanceOf[Publication])
|
|
|
|
val result: Result = items.head.asInstanceOf[Publication]
|
|
|
|
|
|
|
|
result.getPid.asScala.foreach(pid => assertTrue(pid.getQualifier.getClassid.equals("doi")))
|
|
|
|
assertTrue(result.getPid.size() == 1)
|
2022-01-11 16:57:48 +01:00
|
|
|
result.getPid.asScala.foreach(pid =>
|
|
|
|
assertTrue(pid.getValue.equals("10.26850/1678-4618EQJ.v35.1.2010.p41-46".toLowerCase()))
|
|
|
|
)
|
2021-06-30 12:55:37 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-07-14 11:46:31 +02:00
|
|
|
@Test
|
2022-01-11 16:57:48 +01:00
|
|
|
def testLicenseVorClosed(): Unit = {
|
|
|
|
val json = Source
|
|
|
|
.fromInputStream(
|
|
|
|
getClass.getResourceAsStream("/eu/dnetlib/doiboost/crossref/publication_license_vor.json")
|
|
|
|
)
|
|
|
|
.mkString
|
2021-07-14 11:46:31 +02:00
|
|
|
|
|
|
|
assertNotNull(json)
|
|
|
|
assertFalse(json.isEmpty);
|
|
|
|
|
|
|
|
val resultList: List[Oaf] = Crossref2Oaf.convert(json)
|
|
|
|
|
|
|
|
assertTrue(resultList.nonEmpty)
|
|
|
|
|
2022-01-11 16:57:48 +01:00
|
|
|
val item: Result = resultList.filter(p => p.isInstanceOf[Result]).head.asInstanceOf[Result]
|
2021-07-14 11:46:31 +02:00
|
|
|
|
|
|
|
mapper.getSerializationConfig.enable(SerializationConfig.Feature.INDENT_OUTPUT)
|
|
|
|
println(mapper.writeValueAsString(item))
|
|
|
|
|
2022-01-11 16:57:48 +01:00
|
|
|
assertTrue(
|
2023-03-10 16:00:48 +01:00
|
|
|
item.getInstance().asScala exists (i => i.getLicense.getUrl.equals("https://www.springer.com/vor"))
|
2022-01-11 16:57:48 +01:00
|
|
|
)
|
|
|
|
assertTrue(
|
|
|
|
item.getInstance().asScala exists (i => i.getAccessright.getClassid.equals("CLOSED"))
|
|
|
|
)
|
2021-07-14 11:46:31 +02:00
|
|
|
assertTrue(item.getInstance().asScala exists (i => i.getAccessright.getOpenAccessRoute == null))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
2022-01-11 16:57:48 +01:00
|
|
|
def testLicenseOpen(): Unit = {
|
|
|
|
val json = Source
|
|
|
|
.fromInputStream(
|
|
|
|
getClass.getResourceAsStream("/eu/dnetlib/doiboost/crossref/publication_license_open.json")
|
|
|
|
)
|
|
|
|
.mkString
|
2021-07-14 11:46:31 +02:00
|
|
|
|
|
|
|
assertNotNull(json)
|
|
|
|
assertFalse(json.isEmpty);
|
|
|
|
|
|
|
|
val resultList: List[Oaf] = Crossref2Oaf.convert(json)
|
|
|
|
|
|
|
|
assertTrue(resultList.nonEmpty)
|
|
|
|
|
2022-01-11 16:57:48 +01:00
|
|
|
val item: Result = resultList.filter(p => p.isInstanceOf[Result]).head.asInstanceOf[Result]
|
2021-07-14 11:46:31 +02:00
|
|
|
|
2022-01-11 16:57:48 +01:00
|
|
|
assertTrue(
|
|
|
|
item.getInstance().asScala exists (i =>
|
2023-03-10 16:00:48 +01:00
|
|
|
i.getLicense.getUrl.equals(
|
2022-01-11 16:57:48 +01:00
|
|
|
"http://pubs.acs.org/page/policy/authorchoice_ccby_termsofuse.html"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
2021-07-14 11:46:31 +02:00
|
|
|
assertTrue(item.getInstance().asScala exists (i => i.getAccessright.getClassid.equals("OPEN")))
|
2022-01-11 16:57:48 +01:00
|
|
|
assertTrue(
|
2022-01-12 09:40:28 +01:00
|
|
|
item.getInstance().asScala exists (i => i.getAccessright.getOpenAccessRoute == OpenAccessRoute.hybrid)
|
2022-01-11 16:57:48 +01:00
|
|
|
)
|
2021-07-14 11:46:31 +02:00
|
|
|
mapper.getSerializationConfig.enable(SerializationConfig.Feature.INDENT_OUTPUT)
|
|
|
|
println(mapper.writeValueAsString(item))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
2022-01-11 16:57:48 +01:00
|
|
|
def testLicenseEmbargoOpen(): Unit = {
|
|
|
|
val json = Source
|
|
|
|
.fromInputStream(
|
|
|
|
getClass.getResourceAsStream(
|
|
|
|
"/eu/dnetlib/doiboost/crossref/publication_license_embargo_open.json"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.mkString
|
2021-07-14 11:46:31 +02:00
|
|
|
|
|
|
|
assertNotNull(json)
|
|
|
|
assertFalse(json.isEmpty);
|
|
|
|
|
|
|
|
val resultList: List[Oaf] = Crossref2Oaf.convert(json)
|
|
|
|
|
|
|
|
assertTrue(resultList.nonEmpty)
|
|
|
|
|
2022-01-11 16:57:48 +01:00
|
|
|
val item: Result = resultList.filter(p => p.isInstanceOf[Result]).head.asInstanceOf[Result]
|
2021-07-14 11:46:31 +02:00
|
|
|
|
2022-01-11 16:57:48 +01:00
|
|
|
assertTrue(
|
|
|
|
item.getInstance().asScala exists (i =>
|
2023-03-10 16:00:48 +01:00
|
|
|
i.getLicense.getUrl.equals(
|
2022-01-11 16:57:48 +01:00
|
|
|
"https://academic.oup.com/journals/pages/open_access/funder_policies/chorus/standard_publication_model"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
2021-07-14 11:46:31 +02:00
|
|
|
assertTrue(item.getInstance().asScala exists (i => i.getAccessright.getClassid.equals("OPEN")))
|
2022-01-11 16:57:48 +01:00
|
|
|
assertTrue(
|
2022-01-12 09:40:28 +01:00
|
|
|
item.getInstance().asScala exists (i => i.getAccessright.getOpenAccessRoute == OpenAccessRoute.hybrid)
|
2022-01-11 16:57:48 +01:00
|
|
|
)
|
2021-07-14 11:46:31 +02:00
|
|
|
mapper.getSerializationConfig.enable(SerializationConfig.Feature.INDENT_OUTPUT)
|
|
|
|
println(mapper.writeValueAsString(item))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
2022-01-11 16:57:48 +01:00
|
|
|
def testLicenseEmbargo(): Unit = {
|
|
|
|
val json = Source
|
|
|
|
.fromInputStream(
|
|
|
|
getClass.getResourceAsStream(
|
|
|
|
"/eu/dnetlib/doiboost/crossref/publication_license_embargo.json"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.mkString
|
2021-07-14 11:46:31 +02:00
|
|
|
|
|
|
|
assertNotNull(json)
|
|
|
|
assertFalse(json.isEmpty);
|
|
|
|
|
|
|
|
val resultList: List[Oaf] = Crossref2Oaf.convert(json)
|
|
|
|
|
|
|
|
assertTrue(resultList.nonEmpty)
|
|
|
|
|
2022-01-11 16:57:48 +01:00
|
|
|
val item: Result = resultList.filter(p => p.isInstanceOf[Result]).head.asInstanceOf[Result]
|
|
|
|
|
|
|
|
assertTrue(
|
|
|
|
item.getInstance().asScala exists (i =>
|
2023-03-10 16:00:48 +01:00
|
|
|
i.getLicense.getUrl.equals(
|
2022-01-11 16:57:48 +01:00
|
|
|
"https://academic.oup.com/journals/pages/open_access/funder_policies/chorus/standard_publication_model"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
assertTrue(
|
|
|
|
item.getInstance().asScala exists (i => i.getAccessright.getClassid.equals("EMBARGO"))
|
|
|
|
)
|
2021-07-14 11:46:31 +02:00
|
|
|
assertTrue(item.getInstance().asScala exists (i => i.getAccessright.getOpenAccessRoute == null))
|
|
|
|
mapper.getSerializationConfig.enable(SerializationConfig.Feature.INDENT_OUTPUT)
|
|
|
|
println(mapper.writeValueAsString(item))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-07-16 17:30:27 +02:00
|
|
|
@Test
|
2022-01-11 16:57:48 +01:00
|
|
|
def testLicenseEmbargoDateTime(): Unit = {
|
|
|
|
val json = Source
|
|
|
|
.fromInputStream(
|
|
|
|
getClass.getResourceAsStream(
|
|
|
|
"/eu/dnetlib/doiboost/crossref/publication_license_embargo_datetime.json"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.mkString
|
2021-07-16 17:30:27 +02:00
|
|
|
|
|
|
|
assertNotNull(json)
|
|
|
|
assertFalse(json.isEmpty);
|
|
|
|
|
|
|
|
val resultList: List[Oaf] = Crossref2Oaf.convert(json)
|
|
|
|
|
|
|
|
assertTrue(resultList.nonEmpty)
|
|
|
|
|
2022-01-11 16:57:48 +01:00
|
|
|
val item: Result = resultList.filter(p => p.isInstanceOf[Result]).head.asInstanceOf[Result]
|
|
|
|
|
|
|
|
assertTrue(
|
|
|
|
item.getInstance().asScala exists (i =>
|
2023-03-10 16:00:48 +01:00
|
|
|
i.getLicense.getUrl.equals(
|
2022-01-11 16:57:48 +01:00
|
|
|
"https://academic.oup.com/journals/pages/open_access/funder_policies/chorus/standard_publication_model"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
assertTrue(
|
|
|
|
item.getInstance().asScala exists (i => i.getAccessright.getClassid.equals("EMBARGO"))
|
|
|
|
)
|
2021-07-16 17:30:27 +02:00
|
|
|
assertTrue(item.getInstance().asScala exists (i => i.getAccessright.getOpenAccessRoute == null))
|
|
|
|
mapper.getSerializationConfig.enable(SerializationConfig.Feature.INDENT_OUTPUT)
|
|
|
|
println(mapper.writeValueAsString(item))
|
|
|
|
|
|
|
|
}
|
2021-06-18 09:43:32 +02:00
|
|
|
|
2021-11-03 16:45:15 +01:00
|
|
|
@Test
|
2022-01-11 16:57:48 +01:00
|
|
|
def testMultipleURLs(): Unit = {
|
|
|
|
val json = Source
|
|
|
|
.fromInputStream(
|
|
|
|
getClass.getResourceAsStream("/eu/dnetlib/doiboost/crossref/multiple_urls.json")
|
|
|
|
)
|
|
|
|
.mkString
|
2021-11-03 16:45:15 +01:00
|
|
|
|
|
|
|
assertNotNull(json)
|
|
|
|
assertFalse(json.isEmpty);
|
|
|
|
|
|
|
|
val resultList: List[Oaf] = Crossref2Oaf.convert(json)
|
|
|
|
|
|
|
|
assertTrue(resultList.nonEmpty)
|
|
|
|
|
2022-01-11 16:57:48 +01:00
|
|
|
val item: Result = resultList.filter(p => p.isInstanceOf[Result]).head.asInstanceOf[Result]
|
2021-11-03 16:45:15 +01:00
|
|
|
|
|
|
|
assertEquals(1, item.getInstance().size())
|
|
|
|
assertEquals(1, item.getInstance().get(0).getUrl().size())
|
2022-01-11 16:57:48 +01:00
|
|
|
assertEquals(
|
|
|
|
"https://doi.org/10.1016/j.jas.2019.105013",
|
|
|
|
item.getInstance().get(0).getUrl().get(0)
|
|
|
|
)
|
2021-11-03 16:45:15 +01:00
|
|
|
//println(mapper.writeValueAsString(item))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-04-29 13:13:02 +02:00
|
|
|
}
|