Commit 58818bc8 authored by Jonas Waeber's avatar Jonas Waeber

- Add extra tests

- Remove empty lists from more objects
- Extract JSON parse functions
- Add enriched digital metadata
- Add aspect ratio
- Add more failsafes for missing data.
parent 8e408fa9
Pipeline #16409 failed with stages
in 1 minute and 46 seconds
package org.memobase
class InvalidInputException(message: String) : Exception(message)
\ No newline at end of file
......@@ -18,33 +18,46 @@
package org.memobase
import com.beust.klaxon.JsonArray
import com.beust.klaxon.JsonObject
import com.beust.klaxon.Klaxon
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import java.io.StringReader
import java.io.StringWriter
import org.apache.kafka.streams.KeyValue
import org.apache.kafka.streams.StreamsBuilder
import org.apache.kafka.streams.Topology
import org.apache.logging.log4j.LogManager
import org.memobase.helpers.JSON
import org.memobase.model.Report
import org.memobase.model.SearchDoc
import org.memobase.rdf.NS
import org.memobase.settings.SettingsLoader
class KafkaTopology(private val settings: SettingsLoader) {
private val log = LogManager.getLogger("StreamsProcessing")
private val log = LogManager.getLogger("SearchDocService")
private val reportTopic = settings.processReportTopic
private val searchDocTransform = SearchDocTransform(settings.appSettings.getProperty(KEYS.mediaUrlPropName))
fun build(): Topology {
val builder = StreamsBuilder()
val stream = builder.stream<String, String>(settings.inputTopic)
stream
.flatMapValues { value -> parseJson(value) }
.mapValues { value -> unpackJson(value) }
.mapValues { value -> transformJson(value) }
.map { _, value -> KeyValue(value.id, value) }
val transformedStream = stream
.flatMapValues { value -> JSON.parse(value) }
.mapValues { value -> JSON.unpack(value) }
.mapValues { readOnlyKey, value ->
try {
Pair(transformJson(value), Report(readOnlyKey, "SUCCESS", "Transformed message into search doc."))
} catch (ex: InvalidInputException) {
Pair(null, Report(readOnlyKey, "FAILURE", ex.localizedMessage))
}
}
transformedStream
.map { _, value -> KeyValue(value.second.id, value.second.toJson()) }
.to(reportTopic)
transformedStream
.filterNot { _, value -> value.second.status == "FAILURE" }
.map { _, value -> KeyValue(value.first?.id, value.first) }
.mapValues { value ->
val writer = StringWriter()
ObjectMapper().registerKotlinModule().writeValue(writer, value)
......@@ -55,22 +68,6 @@ class KafkaTopology(private val settings: SettingsLoader) {
return builder.build()
}
private fun parseJson(data: String): List<JsonObject> {
val result = Klaxon().parseJsonObject(StringReader(data))
return listOf(result)
}
private fun unpackJson(input: JsonObject): Map<String, JsonObject> {
val graph = input["@graph"] as JsonArray<JsonObject>
return graph.map {
if (it["@type"] == NS.rico + "Record") {
Pair("record", it)
} else {
Pair(it["@id"] as String, it)
}
}.toMap()
}
private fun transformJson(input: Map<String, JsonObject>): SearchDoc {
return searchDocTransform.transform(input)
}
......
......@@ -22,6 +22,7 @@ import com.beust.klaxon.JsonObject
import org.apache.logging.log4j.LogManager
import org.memobase.builders.*
import org.memobase.helpers.*
import org.memobase.model.EnrichedDigitalMetadata
import org.memobase.model.SearchDoc
import org.memobase.rdf.NS
......@@ -29,7 +30,7 @@ class SearchDocTransform(private val mediaUrl: String) {
private val log = LogManager.getLogger("SearchDocTransform")
fun transform(input: Map<String, JsonObject>): SearchDoc {
val record = input["record"] ?: error("No record defined in this message.")
val record = input["record"] ?: throw InvalidInputException("No record defined in the message.")
val digitalObject =
input.values.firstOrNull { it["@type"] == NS.rico + "Instantiation" && it["type"] == "digitalObject" }
val physicalObject =
......@@ -148,11 +149,33 @@ class SearchDocTransform(private val mediaUrl: String) {
""
}
val digitalObjectValues = digitalObject.let {
if (it != null) {
val width = it.getOrDefault("width", "") as String
val height = it.getOrDefault("height", "") as String
EnrichedDigitalMetadata(
hasFormat = it.getOrDefault("hasFormat", "") as String,
isDistributedOn = it.getOrDefault("isDistributedOn", "") as String,
hasMimeType = it.getOrDefault("hasMimeType", "") as String,
height = height,
width = width,
aspectRatio = AspectRatio.asFraction(width, height),
mediaResourceDescription = it.getOrDefault("mediaResourceDescription", "") as String,
orientation = it.getOrDefault("orientation", "") as String,
hasColourContent = it.getOrDefault("P60558", "") as String,
componentColor = Extract.listOfStrings(digitalObject?.get("componentColor"))
)
} else {
EnrichedDigitalMetadata()
}
}
return SearchDoc(
title = Extract.typedEntityByType(recordTitles, "type", "main", "title"),
seriesTitle = Extract.typedEntityByType(recordTitles, "type", "series", "title"),
broadcastTitle = Extract.typedEntityByType(recordTitles, "type", "broadcast", "title"),
type = record["type"] as String,
type = record.getOrDefault("type", "Foto") as String,
sourceID = try {
Extract.extractIdValue(recordIdentifiers, KEYS.IdentifierType.original) ?: "NoSourceIdFound"
} catch (ex: NoSuchElementException) {
......@@ -163,7 +186,7 @@ class SearchDocTransform(private val mediaUrl: String) {
abstract = Extract.languageContainer("abstract", record["abstract"]),
id = id,
institution = Meta.extractInstitution(record),
recordSet = Meta.extractRecordSet(record)[0],
recordSet = Meta.extractRecordSet(record),
descriptiveNote = Extract.languageContainer("descriptiveNote", record["descriptiveNote"]),
scopeAndContent = Extract.languageContainer("scopeAndContent", record["scopeAndContent"]),
relatedMaterial = Extract.languageContainer("relation", record["relation"]),
......@@ -217,6 +240,8 @@ class SearchDocTransform(private val mediaUrl: String) {
usageDigital = usageDigital,
usageDigitalGroup = usageDigital.map { ReuseStatementMap.getValue(it) },
digital = digitalObjectValues,
// PHYSICAL
accessPhysical = accessPhysical,
durationPhysical = Extract.listOfStrings(physicalObject?.get("duration")),
......
package org.memobase.helpers
import java.lang.NumberFormatException
object AspectRatio {
private fun gcd(a: Long, b: Long): Long {
return if (b == 0L) a else gcd(b, a % b)
}
private fun removeDot(value: String): String {
return if (value.contains(".")) {
value.substringBefore(".")
} else {
value
}
}
fun asFraction(a: String, b: String): String {
val editedA = removeDot(a)
val editedB = removeDot(b)
val aLong = try {
editedA.toLong()
} catch (ex: NumberFormatException) {
return ""
}
val bLong = try {
editedB.toLong()
} catch (ex: NumberFormatException) {
return ""
}
val gcd = gcd(aLong, bLong)
return (aLong / gcd).toString() + "/" + (bLong / gcd).toString()
}
}
\ No newline at end of file
package org.memobase.helpers
import com.beust.klaxon.JsonArray
import com.beust.klaxon.JsonObject
import com.beust.klaxon.Klaxon
import org.memobase.rdf.NS
import java.io.StringReader
object JSON {
private val klaxon = Klaxon()
fun parse(data: String): List<JsonObject> {
val result = Klaxon().parseJsonObject(StringReader(data))
return listOf(result)
}
fun unpack(input: JsonObject): Map<String, JsonObject> {
val graph = input["@graph"] as JsonArray<JsonObject>
return graph.map {
if (it["@type"] == NS.rico + "Record") {
Pair("record", it)
} else {
Pair(it["@id"] as String, it)
}
}.toMap()
}
}
\ No newline at end of file
......@@ -8,7 +8,7 @@ import org.memobase.model.LanguageContainer
import org.memobase.rdf.NS
/**
Extraction helpers for institutions and record sets.
Extraction helpers for institutions and record sets.
*/
object Meta {
private val log = LogManager.getLogger("InstitutionExtraction")
......@@ -17,8 +17,22 @@ object Meta {
return extract(KEYS.heldBy, record)
}
fun extractRecordSet(record: JsonObject): List<FacettedContainer> {
return extract(KEYS.isPartOf, record)
fun extractRecordSet(record: JsonObject): FacettedContainer {
val containers = extract(KEYS.isPartOf, record)
return if (containers.isNotEmpty()) {
containers[0]
} else {
FacettedContainer(
LanguageContainer(
listOf("Unknown RecordSet"),
listOf("Unknown RecordSet"),
listOf("Unknown RecordSet"),
listOf("Unknown RecordSet")
),
"UnknownRecordSet",
listOf("UnknownRecordSet")
)
}
}
// TODO: Implement function to extract labels from elastic index.
......
......@@ -18,6 +18,9 @@
package org.memobase.model
import com.fasterxml.jackson.annotation.JsonInclude
@JsonInclude(JsonInclude.Include.NON_EMPTY)
data class AgentWithRelationContainer(
val name: LanguageContainer,
val relation: LanguageContainer?,
......
......@@ -20,7 +20,7 @@ package org.memobase.model
import com.fasterxml.jackson.annotation.JsonInclude
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
data class DateContainer(
val date: String,
val sort: String?,
......
package org.memobase.model
import com.fasterxml.jackson.annotation.JsonInclude
@JsonInclude(JsonInclude.Include.NON_EMPTY)
data class EnrichedDigitalMetadata(
val isDistributedOn: String = "",
val hasMimeType: String = "",
val hasFormat: String = "",
val height: String = "",
val width: String = "",
val aspectRatio: String = "",
val orientation: String = "",
val mediaResourceDescription: String = "",
val hasColourContent: String = "",
val componentColor: List<String> = emptyList()
)
\ No newline at end of file
......@@ -20,7 +20,7 @@ package org.memobase.model
import com.fasterxml.jackson.annotation.JsonInclude
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
data class FacettedContainer(
val name: LanguageContainer,
val filter: String?,
......
/*
* Media Linker
* Copyright (C) 2020 Memoriav
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.memobase.model
import com.beust.klaxon.Klaxon
import java.time.LocalDateTime
data class Report(
val id: String,
val status: String,
val message: String,
val step: String = "search-doc-service",
val timestamp: String = LocalDateTime.now().toString()
) {
fun toJson(): String {
return Klaxon().toJsonString(this)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Report
if (id != other.id) return false
if (status != other.status) return false
if (message != other.message) return false
if (step != other.step) return false
return true
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + status.hashCode()
result = 31 * result + message.hashCode()
result = 31 * result + step.hashCode()
return result
}
}
......@@ -91,6 +91,9 @@ data class SearchDoc(
val digitalObjectNote: List<LanguageContainer>,
val usageConditionsDigital: List<LanguageContainer>,
// enriched from digital object
val digital: EnrichedDigitalMetadata,
// Physical Object
val durationPhysical: List<String>,
val callNumber: List<String>,
......
......@@ -18,6 +18,9 @@
package org.memobase.model
import com.fasterxml.jackson.annotation.JsonInclude
@JsonInclude(JsonInclude.Include.NON_EMPTY)
data class SuggestContainer(
val title: List<String>,
val seriesTitle: List<String>,
......
......@@ -76,12 +76,12 @@ class IntegrationTest {
2,
"kafkaTest1.yml",
"key"
)/*,
),
TestParam(
"test-3",
3,
"kafkaTest1.yml",
"key"
)*/
)
)
}
package org.memobase
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.memobase.helpers.AspectRatio
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestFractions {
@Test
fun `test 16-9 aspect ratios`() {
val result = AspectRatio.asFraction("1280", "720")
assertThat(result).isEqualTo("16/9")
}
@Test
fun `test 21-9 aspect ratios`() {
val result = AspectRatio.asFraction("2520", "1080")
assertThat(result).isEqualTo("7/3")
}
@Test
fun `test 4-3 aspect ratios`() {
val result = AspectRatio.asFraction("1280", "960")
assertThat(result).isEqualTo("4/3")
}
@Test
fun `test 3-2 aspect ratios`() {
val result = AspectRatio.asFraction("1260", "840")
assertThat(result).isEqualTo("3/2")
}
}
\ No newline at end of file
package org.memobase
import com.beust.klaxon.json
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.assertThrows
import org.memobase.helpers.JSON
import java.io.File
import java.io.FileInputStream
import java.nio.charset.Charset
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestTransform {
private val dataPath = "src/test/resources/data/transformer"
@Test
fun `test missing record`() {
val searchDoc = SearchDocTransform("https://media.memobase.k8s.unibas.ch/memo/")
val input = mapOf(Pair("", json { obj() }))
assertThrows<InvalidInputException> { searchDoc.transform(input) }
}
@Test
fun `test enriched digital metadata record`() {
val searchDoc = SearchDocTransform("https://media.memobase.k8s.unibas.ch/memo/")
val input = JSON.unpack(JSON.parse(
FileInputStream(File("$dataPath/enrich_digital_metadata_record.json")).readAllBytes().toString(
Charset.defaultCharset()
)
)[0])
val values = searchDoc.transform(input)
}
}
\ No newline at end of file
{"title":[{"de":[],"fr":[],"it":[],"un":["Bern: Altstadt, obere; Bundesplatz 4; Bärenplatz (31); Käfiggässchen; -- Fuhrwerk; Automobil; Lastwagen; Transport, Verkehr; Strassenbeleuchtung"]}],"type":"Foto","sourceID":"216133","id":"https://memobase.ch/record/Burgerbib-Krebser-216133","descriptiveNote":[{"de":[],"fr":[],"it":[],"un":["Datierung: Bundesplatz / Büren-Besitzung<br>"]}],"rightsHolder":[{"de":[],"fr":[],"it":[],"un":["Burgerbibliothek Bern"]}],"sameAs":["http://katalog.burgerbib.ch/detail.aspx?ID=216133"],"keywords":[{"name":{"de":[],"fr":[],"it":[],"un":["Transport, Verkehr"]},"facet":["Transport, Verkehr"]}],"personCreator":[{"name":{"de":[],"fr":[],"it":[],"un":["Anonym"]},"relation":{"de":[],"fr":[],"it":[],"un":["author"]},"filter":"Anonym","facet":["0~A~","1~A~Anonym~"]}],"personContributor":[{"name":{"de":[],"fr":[],"it":[],"un":["Familie Krebser"]},"relation":{"de":[],"fr":[],"it":[],"un":["Collector/Sammler"]},"filter":"Familie Krebser","facet":["0~F~","1~F~Familie Krebser~"]}],"personsFacet":["0~A~","1~A~Anonym~","0~F~","1~F~Familie Krebser~"],"placeRelated":[{"name":{"de":[],"fr":[],"it":[],"un":["Käfiggässchen"]},"filter":"Käfiggässchen","facet":["0~K~","1~K~Käfiggässchen~"]}],"placeFacet":["0~K~","1~K~Käfiggässchen~"],"dateCreated":[{"date":"1900/1909","facet":[]}],"institution":[{"name":{"de":[],"fr":[],"it":[],"un":[]},"facet":["Burgerbib"]}],"recordSet":{"name":{"de":[],"fr":[],"it":[],"un":[]},"facet":["Burgerbib-Krebser"]},"memoriavClaim":true,"locator":"https://media.memobase.k8s.unibas.ch/memo/Burgerbib-Krebser-216133-1","callNumber":["Historische Sammlung Krebser 13/2"],"physicalCharacteristics":[{"de":[],"fr":[],"it":[],"un":["Weite: 85 mm","Höhe: 100 mm"]}],"format":[{"name":{"de":["Glasplatte"],"fr":[],"it":[],"un":[]},"facet":["http://www.wikidata.org/entity/Q1138868"]},{"name":{"de":[],"fr":[],"it":[],"un":["Glasplatte"]},"facet":[]}],"published":false,"suggest":{"title":["Bern: Altstadt, obere; Bundesplatz 4; Bärenplatz (31); Käfiggässchen; -- Fuhrwerk; Automobil; Lastwagen; Transport, Verkehr; Strassenbeleuchtung"],"seriesTitle":[],"broadcastTitle":[],"keywords":["Transport, Verkehr"]}}
\ No newline at end of file
{"title":[{"de":[],"fr":[],"it":[],"un":["Bern: Altstadt, obere; Bundesplatz 4; Bärenplatz (31); Käfiggässchen; -- Fuhrwerk; Automobil; Lastwagen; Transport, Verkehr; Strassenbeleuchtung"]}],"type":"Foto","sourceID":"216133","id":"https://memobase.ch/record/Burgerbib-Krebser-216133","descriptiveNote":[{"de":[],"fr":[],"it":[],"un":["Datierung: Bundesplatz / Büren-Besitzung<br>"]}],"rightsHolder":[{"de":[],"fr":[],"it":[],"un":["Burgerbibliothek Bern"]}],"sameAs":["http://katalog.burgerbib.ch/detail.aspx?ID=216133"],"keywords":[{"name":{"de":[],"fr":[],"it":[],"un":["Transport, Verkehr"]},"facet":["Transport, Verkehr"]}],"personCreator":[{"name":{"de":[],"fr":[],"it":[],"un":["Anonym"]},"relation":{"de":[],"fr":[],"it":[],"un":["author"]},"filter":"Anonym","facet":["0~A~","1~A~Anonym~"]}],"personContributor":[{"name":{"de":[],"fr":[],"it":[],"un":["Familie Krebser"]},"relation":{"de":[],"fr":[],"it":[],"un":["Collector/Sammler"]},"filter":"Familie Krebser","facet":["0~F~","1~F~Familie Krebser~"]}],"personsFacet":["0~A~","1~A~Anonym~","0~F~","1~F~Familie Krebser~"],"placeRelated":[{"name":{"de":[],"fr":[],"it":[],"un":["Käfiggässchen"]},"filter":"Käfiggässchen","facet":["0~K~","1~K~Käfiggässchen~"]}],"placeFacet":["0~K~","1~K~Käfiggässchen~"],"dateCreated":[{"date":"1900/1909"}],"institution":[{"name":{"de":[],"fr":[],"it":[],"un":[]},"facet":["Burgerbib"]}],"recordSet":{"name":{"de":[],"fr":[],"it":[],"un":[]},"facet":["Burgerbib-Krebser"]},"memoriavClaim":true,"locator":"https://media.memobase.k8s.unibas.ch/memo/Burgerbib-Krebser-216133-1","digital":{},"callNumber":["Historische Sammlung Krebser 13/2"],"physicalCharacteristics":[{"de":[],"fr":[],"it":[],"un":["Weite: 85 mm","Höhe: 100 mm"]}],"format":[{"name":{"de":["Glasplatte"],"fr":[],"it":[],"un":[]},"facet":["http://www.wikidata.org/entity/Q1138868"]},{"name":{"de":[],"fr":[],"it":[],"un":["Glasplatte"]}}],"published":false,"suggest":{"title":["Bern: Altstadt, obere; Bundesplatz 4; Bärenplatz (31); Käfiggässchen; -- Fuhrwerk; Automobil; Lastwagen; Transport, Verkehr; Strassenbeleuchtung"],"keywords":["Transport, Verkehr"]}}
\ No newline at end of file
{"title":[{"de":[],"fr":[],"it":[],"un":["Anonym -- Armut"]}],"type":"Foto","sourceID":"208576","id":"https://memobase.ch/record/Burgerbib-Krebser-208576","abstract":[{"de":[],"fr":[],"it":[],"un":["Bildeintrag: auf der Verpackung: 49543<br>"]}],"descriptiveNote":[{"de":[],"fr":[],"it":[],"un":["Datierung: Negativnummer<br>"]}],"rightsHolder":[{"de":[],"fr":[],"it":[],"un":["Burgerbibliothek Bern"]}],"sameAs":["http://katalog.burgerbib.ch/detail.aspx?ID=208576"],"keywords":[{"name":{"de":[],"fr":[],"it":[],"un":["Armut"]},"facet":["Armut"]}],"personCreator":[{"name":{"de":[],"fr":[],"it":[],"un":["Moeglé, Jean"]},"relation":{"de":[],"fr":[],"it":[],"un":["author"]},"filter":"Moeglé, Jean","facet":["0~M~","1~M~Moeglé, Jean~"]}],"personContributor":[{"name":{"de":[],"fr":[],"it":[],"un":["Familie Krebser"]},"relation":{"de":[],"fr":[],"it":[],"un":["Collector/Sammler"]},"filter":"Familie Krebser","facet":["0~F~","1~F~Familie Krebser~"]}],"personsFacet":["0~F~","1~F~Familie Krebser~","0~M~","1~M~Moeglé, Jean~"],"dateCreated":[{"date":"20. Jh.","facet":[]}],"institution":[{"name":{"de":[],"fr":[],"it":[],"un":[]},"facet":["Burgerbib"]}],"recordSet":{"name":{"de":[],"fr":[],"it":[],"un":[]},"facet":["Burgerbib-Krebser"]},"memoriavClaim":true,"locator":"https://media.memobase.k8s.unibas.ch/memo/Burgerbib-Krebser-208576-1","callNumber":["Historische Sammlung Krebser 64/21"],"physicalCharacteristics":[{"de":[],"fr":[],"it":[],"un":["Weite: 165 mm","Höhe: 120 mm"]}],"format":[{"name":{"de":["Glasplatte"],"fr":[],"it":[],"un":[]},"facet":["http://www.wikidata.org/entity/Q1138868"]},{"name":{"de":[],"fr":[],"it":[],"un":["Glasplatte"]},"facet":[]}],"published":false,"suggest":{"title":["Anonym -- Armut"],"seriesTitle":[],"broadcastTitle":[],"keywords":["Armut"]}}
\ No newline at end of file
{"title":[{"de":[],"fr":[],"it":[],"un":["Anonym -- Armut"]}],"type":"Foto","sourceID":"208576","id":"https://memobase.ch/record/Burgerbib-Krebser-208576","abstract":[{"de":[],"fr":[],"it":[],"un":["Bildeintrag: auf der Verpackung: 49543<br>"]}],"descriptiveNote":[{"de":[],"fr":[],"it":[],"un":["Datierung: Negativnummer<br>"]}],"rightsHolder":[{"de":[],"fr":[],"it":[],"un":["Burgerbibliothek Bern"]}],"sameAs":["http://katalog.burgerbib.ch/detail.aspx?ID=208576"],"keywords":[{"name":{"de":[],"fr":[],"it":[],"un":["Armut"]},"facet":["Armut"]}],"personCreator":[{"name":{"de":[],"fr":[],"it":[],"un":["Moeglé, Jean"]},"relation":{"de":[],"fr":[],"it":[],"un":["author"]},"filter":"Moeglé, Jean","facet":["0~M~","1~M~Moeglé, Jean~"]}],"personContributor":[{"name":{"de":[],"fr":[],"it":[],"un":["Familie Krebser"]},"relation":{"de":[],"fr":[],"it":[],"un":["Collector/Sammler"]},"filter":"Familie Krebser","facet":["0~F~","1~F~Familie Krebser~"]}],"personsFacet":["0~F~","1~F~Familie Krebser~","0~M~","1~M~Moeglé, Jean~"],"dateCreated":[{"date":"20. Jh."}],"institution":[{"name":{"de":[],"fr":[],"it":[],"un":[]},"facet":["Burgerbib"]}],"recordSet":{"name":{"de":[],"fr":[],"it":[],"un":[]},"facet":["Burgerbib-Krebser"]},"memoriavClaim":true,"locator":"https://media.memobase.k8s.unibas.ch/memo/Burgerbib-Krebser-208576-1","digital":{},"callNumber":["Historische Sammlung Krebser 64/21"],"physicalCharacteristics":[{"de":[],"fr":[],"it":[],"un":["Weite: 165 mm","Höhe: 120 mm"]}],"format":[{"name":{"de":["Glasplatte"],"fr":[],"it":[],"un":[]},"facet":["http://www.wikidata.org/entity/Q1138868"]},{"name":{"de":[],"fr":[],"it":[],"un":["Glasplatte"]}}],"published":false,"suggest":{"title":["Anonym -- Armut"],"keywords":["Armut"]}}
\ No newline at end of file
{
"@graph": [
{
"@id": "https://memobase.ch/digital/LS-film-162354-1",
"@type": "https://www.ica.org/standards/RiC/ontology#Instantiation",
"isDistributedOn": "vimeo",
"locator": "https://vimeo.com/290901385",
"contains": "https://memobase.ch/digital/LS-film-162354-1/derived",
"hasDerivedInstantiation": "https://memobase.ch/digital/LS-film-162354-1/derived",
"identifiedBy": "https://memobase.ch/digital/LS-film-162354-1#genidd0f079ba-14d3-4ab6-993e-8cf4ba606329",
"instantiates": "https://memobase.ch/record/LS-film-162354",
"isDerivedFromInstantiation": "https://memobase.ch/physical/LS-film-162354-1",
"regulatedBy": "https://memobase.ch/digital/LS-film-162354-1#genidc092d79e-2402-441a-85c4-fc2c0c79c10d",
"type": "digitalObject"
},
{
"@id": "https://memobase.ch/digital/LS-film-162354-1#genidc092d79e-2402-441a-85c4-fc2c0c79c10d",
"@type": "https://www.ica.org/standards/RiC/ontology#Rule",
"name": "public",
"regulates": "https://memobase.ch/digital/LS-film-162354-1",
"type": "access"
},
{
"@id": "https://memobase.ch/digital/LS-film-162354-1#genidd0f079ba-14d3-4ab6-993e-8cf4ba606329",
"@type": "https://www.ica.org/standards/RiC/ontology#Identifier",
"identifier": "LS-film-162354-1",
"type": "main"
},
{
"@id": "https://memobase.ch/digital/LS-film-162354-1/derived",
"@type": "https://www.ica.org/standards/RiC/ontology#Instantiation",
"P60558": "TrueColorAlpha",
"hasFormat": "fmt/12",
"hasMimeType": "image/png",
"height": "138.0",
"isDistributedOn": "image",
"locator": "https://memobase.ch/digital/LS-film-162354-1/derived/binary",
"orientation": "Undefined",
"width": "138.0",
"componentColor": [
"DB7093",
"8E236B",
"545454",
"CC3299",
"FFFAFA",
"9F5F9F",
"000000",
"FFB6C1",
"FFFFFF"
],
"instantiates": "https://memobase.ch/record/LS-film-162354",
"isDerivedFromInstantiation": "https://memobase.ch/digital/LS-film-162354-1",
"type": "thumbnail"
},
{
"@id": "https://memobase.ch/physical/LS-film-162354-1",
"@type": "https://www.ica.org/standards/RiC/ontology#Instantiation",
"P60558": "farbig",
"duration": "00:11:00",
"hasCarrierType": [
"https://memobase.ch/physical/LS-film-162354-1#genid71c457b5-9978-45e5-9e15-9c3b5a7b173b",
"https://memobase.ch/physical/LS-film-162354-1#genidf447df3e-c4af-49ad-9a51-59a429244a0b"
],
"hasDerivedInstantiation": "https://memobase.ch/digital/LS-film-162354-1",
"identifiedBy": "https://memobase.ch/physical/LS-film-162354-1#genidd82691a4-0135-44aa-b33d-7a695df4c7cd",
"instantiates": "https://memobase.ch/record/LS-film-162354",
"physicalCharacteristics": "Verfahren: Länge: 119.00<br>Ton: Magnetton<br>Seitenverhältnis: 1.33<br>Positiv",
"regulatedBy": "https://memobase.ch/physical/LS-film-162354-1#genid06a87665-b665-4d59-a041-3f2c3eaf17fa",
"type": "physicalObject"
},
{
"@id": "https://memobase.ch/physical/LS-film-162354-1#genid06a87665-b665-4d59-a041-3f2c3eaf17fa",
"@type": "https://www.ica.org/standards/RiC/ontology#Rule",
"name": "onsite",
"regulates": "https://memobase.ch/physical/LS-film-162354-1",
"type": "access"
},
{
"@id": "https://memobase.ch/physical/LS-film-162354-1#genid71c457b5-9978-45e5-9e15-9c3b5a7b173b",
"@type": "https://www.ica.org/standards/RiC/ontology#CarrierType",
"sameAs": "http://www.wikidata.org/entity/Q194383",
"name": [
{
"@language": "it",
"@value": "16 millimetri"
},
{
"@language": "fr",
"@value": "Format 16 mm"