Commit 9146aa8b authored by Jonas Waeber's avatar Jonas Waeber

Finish implementation for institution search doc.

parent 1b42a14b
......@@ -50,6 +50,8 @@ dependencies {
// JSON Parser
implementation 'com.beust:klaxon:5.2'
// CSV Reader
implementation("com.github.doyaaaaaken:kotlin-csv-jvm:0.7.3")
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
implementation "org.jetbrains.kotlin:kotlin-script-runtime:1.3.71"
......
......@@ -25,4 +25,11 @@ spec:
name: "{{ .Values.kafkaConfigs }}"
- configMapRef:
name: "{{ .Values.deploymentName}}-app-config"
volumeMounts:
- name: instituion-type-labels
mountPath: "/configs/institution_types/"
volumes:
- name: instituion-type-labels
configMap:
name: "{{ .Values.instutionTypeLabels }}"
restartPolicy: Always
......@@ -9,4 +9,6 @@ outputTopic: search-doc-output-documents
inputTopic: search-doc-input-documents
reportingTopic: postprocessing-reporting
instutionTypeLabels: institution-type-labels
mediaServerUrl: https://media.memobase.k8s.unibas.ch/memo/
\ No newline at end of file
......@@ -18,24 +18,84 @@
package org.memobase
import com.beust.klaxon.JsonArray
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.InstitutionSearchDoc
import org.memobase.model.Schema
import org.memobase.model.SearchDoc
import org.memobase.rdf.NS
import org.memobase.model.*
class InstitutionSearchDocBuilder {
class InstitutionSearchDocBuilder(path: String) {
private val log = LogManager.getLogger("InstitutionSearchDocBuilder")
fun transform(input: Map<String, JsonObject>): Schema {
private val institutionTypeMapper = InstitutionTypeMapper(path)
fun transform(key: String, input: Map<String, JsonObject>): Schema {
val institution = input["institution"] ?: throw InvalidInputException("No institution entity found in message $key.")
val identifiers = Filter.entitiesByProperty(KEYS.identifiedBy, institution, input)
val locations = Filter.entitiesByProperty(KEYS.hasLocation, institution, input)
val cantons = mutableListOf<JsonObject>()
input.values.forEach {
if (it[KEYS.ricoType] == KEYS.LocationType.canton) {
cantons.add(it)
}
}
return InstitutionSearchDoc(
val type = institution[KEYS.wikidataInstance].let {
when(it) {
is String -> listOf(institutionTypeMapper.getValue(it))
is JsonArray<*> -> it.map { any -> institutionTypeMapper.getValue(any as String) }
else -> {
log.error("Found no institution types on institution $key")
emptyList()
}
}
}
val name = Extract.languageContainer("institution", institution[KEYS.name]).let { names ->
when {
names.isEmpty() -> {
LanguageContainer.placeholder("NoNameFound")
}
names.size == 1 -> {
names[0]
}
else -> {
names.reduce { acc, languageContainer -> acc.merge(languageContainer) }
}
}
}
val canton = cantons.map {
it[KEYS.name].let { name ->
Extract.languageContainer("canton", name).reduce { acc, languageContainer -> acc.merge(languageContainer) }
}
}.let { c ->
when {
c.isEmpty() -> {
listOf(LanguageContainer.placeholder("NoCantonNameFound"))
}
else -> c
}
}
return InstitutionSearchDoc(
institutionId = Extract.extractIdValue(identifiers, KEYS.IdentifierType.main) ?: "NoIdentifierFound",
published = institution[KEYS.isPublished].let {
when (it) {
is String -> it.toBoolean()
else -> {
log.error("Found no isPublished property on institution $key. Set to false.")
false
}
}
},
type = type,
name = name,
documentType = listOf(LanguageContainer.placeholder("PLACEHOLDER")),
keyVisualLink = "placeholderlink",
canton = canton,
numberOfRecordSets = 0,
numberOfDocuments = 0
)
}
}
......@@ -18,7 +18,6 @@
package org.memobase
import com.beust.klaxon.JsonObject
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import java.io.StringWriter
......@@ -30,10 +29,9 @@ import org.apache.kafka.streams.kstream.Predicate
import org.apache.logging.log4j.LogManager
import org.memobase.helpers.Default
import org.memobase.helpers.JSON
import org.memobase.model.InstitutionSearchDoc
import org.memobase.helpers.KEYS
import org.memobase.model.Report
import org.memobase.model.Schema
import org.memobase.model.SearchDoc
import org.memobase.settings.SettingsLoader
class KafkaTopology(private val settings: SettingsLoader) {
......@@ -41,8 +39,8 @@ class KafkaTopology(private val settings: SettingsLoader) {
private val reportTopic = settings.processReportTopic
private val searchDocTransform = SearchDocTransform(settings.appSettings.getProperty(KEYS.mediaUrlPropName))
private val institutionSearchDoc = InstitutionSearchDocBuilder()
private val searchDocTransform = SearchDocTransform(settings.appSettings.getProperty(KEYS.SettingsProps.mediaUrl))
private val institutionSearchDoc = InstitutionSearchDocBuilder(settings.appSettings.getProperty(KEYS.SettingsProps.institutionTypeLabelsPath))
fun build(): Topology {
val builder = StreamsBuilder()
......@@ -71,7 +69,7 @@ class KafkaTopology(private val settings: SettingsLoader) {
val institutionStream = branchedStream[1]
.mapValues { readOnlyKey, value ->
try {
Pair(institutionSearchDoc.transform(value), Report(readOnlyKey, "SUCCESS", "Transformed message into search doc."))
Pair(institutionSearchDoc.transform(readOnlyKey, value), Report(readOnlyKey, "SUCCESS", "Transformed message into search doc."))
} catch (ex: InvalidInputException) {
Pair(Default.institutionSearchDoc, Report(readOnlyKey, "FAILURE", ex.localizedMessage))
}
......@@ -86,12 +84,12 @@ class KafkaTopology(private val settings: SettingsLoader) {
private fun outputStreams(stream: KStream<String, Pair<Schema, Report>>) {
stream
.map { _, value -> KeyValue(value.second.id, value.second.toJson()) }
.mapValues { value -> value.second.toJson() }
.to(reportTopic)
stream
.filterNot { _, value -> value.second.status == "FAILURE" }
.map { _, value -> KeyValue(value.first.id, value.first) }
.mapValues { value -> value.first }
.mapValues { value ->
val writer = StringWriter()
ObjectMapper().registerKotlinModule().writeValue(writer, value)
......
......@@ -21,6 +21,7 @@ package org.memobase
import kotlin.system.exitProcess
import org.apache.kafka.streams.KafkaStreams
import org.apache.logging.log4j.LogManager
import org.memobase.helpers.KEYS
import org.memobase.settings.SettingsLoader
class Service(file: String = "app.yml") {
......@@ -28,7 +29,8 @@ class Service(file: String = "app.yml") {
val settings = SettingsLoader(
listOf(
KEYS.mediaUrlPropName
KEYS.SettingsProps.institutionTypeLabelsPath,
KEYS.SettingsProps.mediaUrl
),
file,
useStreamsConfig = true
......
......@@ -20,7 +20,7 @@ package org.memobase.builders
import com.beust.klaxon.JsonObject
import org.apache.logging.log4j.LogManager
import org.memobase.KEYS
import org.memobase.helpers.KEYS
import org.memobase.helpers.Extract
import org.memobase.helpers.FacetBuildHelpers
import org.memobase.model.AgentWithRelationContainer
......
......@@ -2,7 +2,7 @@ package org.memobase.builders
import com.beust.klaxon.JsonObject
import org.apache.logging.log4j.LogManager
import org.memobase.KEYS
import org.memobase.helpers.KEYS
import org.memobase.helpers.DateFacetBuildHelpers
import org.memobase.model.DateContainer
import org.memobase.rdf.NS
......
......@@ -19,7 +19,7 @@
package org.memobase.builders
import com.beust.klaxon.JsonObject
import org.memobase.KEYS
import org.memobase.helpers.KEYS
import org.memobase.helpers.Extract
import org.memobase.model.FacettedContainer
import org.memobase.rdf.NS
......
......@@ -20,7 +20,7 @@ package org.memobase.builders
import com.beust.klaxon.JsonObject
import org.apache.logging.log4j.LogManager
import org.memobase.KEYS
import org.memobase.helpers.KEYS
import org.memobase.helpers.FacetBuildHelpers
import org.memobase.rdf.NS
......
......@@ -20,7 +20,7 @@ package org.memobase.builders
import com.beust.klaxon.JsonObject
import org.apache.logging.log4j.LogManager
import org.memobase.KEYS
import org.memobase.helpers.KEYS
import org.memobase.helpers.FacetBuildHelpers
import org.memobase.rdf.NS
......
......@@ -19,7 +19,7 @@
package org.memobase.builders
import com.beust.klaxon.JsonObject
import org.memobase.KEYS
import org.memobase.helpers.KEYS
import org.memobase.helpers.Extract
import org.memobase.model.SuggestContainer
import org.memobase.rdf.NS
......
......@@ -4,12 +4,11 @@ import org.memobase.model.*
object Default {
val institutionSearchDoc = InstitutionSearchDoc(
"UnknownId",
false,
LanguageContainer(emptyList(), emptyList(), emptyList(), emptyList()),
LanguageContainer(emptyList(), emptyList(), emptyList(), emptyList()),
LanguageContainer.EMPTY,
listOf(LanguageContainer.EMPTY),
emptyList(),
emptyList(),
"",
......
......@@ -20,7 +20,6 @@ package org.memobase.helpers
import com.beust.klaxon.JsonObject
import org.apache.logging.log4j.LogManager
import org.memobase.KEYS
/**
* Helper functions to build hierarchical facet values for places and persons.
......
/*
* search-doc-service
* 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.helpers
import org.memobase.model.LanguageContainer
class InstitutionTypeMapper(path: String) {
private val labels = LoadFile.readLabelFile(path)
fun getValue(uri: String): LanguageContainer {
val q = uri.substringAfterLast("/")
return labels[q] ?: LanguageContainer.DEFAULT
}
}
......@@ -16,10 +16,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.memobase
package org.memobase.helpers
object KEYS {
const val mediaUrlPropName = "media.url"
object SettingsProps {
const val mediaUrl = "media.url"
const val institutionTypeLabelsPath = "institutionTypeLabelsPath"
}
const val entityId = "@id"
const val atType = "@type"
......@@ -34,6 +38,7 @@ object KEYS {
const val agentIsTargetOfCreationRelation = "agentIsTargetOfCreationRelation"
const val hasSubject = "hasSubject"
const val hasLocation = "hasLocation"
const val placeOfCapture = "P60556"
const val spatial = "spatial"
const val producer = "P60441"
......@@ -45,6 +50,8 @@ object KEYS {
const val contributor = "contributor"
const val creator = "creator"
const val identifiedBy = "identifiedBy"
const val Person = "Person"
const val CorporateBody = "CorporateBody"
const val Agent = "Agent"
......@@ -64,6 +71,14 @@ object KEYS {
const val Concept = "Concept"
const val wikidataInstance = "P31"
const val missingLabelDe = "FEHLENDES LABEL"
const val missingLabelFr = "L'ÉTIQUETTE MANQUANTE"
const val missingLabelIt = "GALATEO MANCANTE"
const val missingLabelEn = "MISSING LABEL"
object TitleTypes {
const val main = "main"
const val series = "series"
......@@ -75,4 +90,11 @@ object KEYS {
const val original = "original"
const val callNumber = "callNumber"
}
object LocationType {
const val canton = "canton"
const val municipality = "municipality"
const val memobaseInstitution = "memobaseInstitution"
const val memobaseProject = "memobaseProject"
}
}
package org.memobase.helpers
import com.github.doyaaaaaken.kotlincsv.dsl.csvReader
import org.memobase.model.LanguageContainer
import java.io.File
object LoadFile {
private val csv = csvReader()
fun readLabelFile(path: String): Map<String, LanguageContainer> {
val labelList = csv.readAll(File(path))
val labelsMap = mutableMapOf<String, LanguageContainer>()
for (row in labelList.listIterator(1)) {
labelsMap[row[0]] = LanguageContainer(listOf(row[1]), listOf(row[2]), listOf(row[3]), emptyList())
}
return labelsMap
}
}
\ No newline at end of file
......@@ -2,7 +2,6 @@ package org.memobase.helpers
import com.beust.klaxon.JsonObject
import org.apache.logging.log4j.LogManager
import org.memobase.KEYS
import org.memobase.model.FacettedContainer
import org.memobase.model.LanguageContainer
import org.memobase.rdf.NS
......
......@@ -10,7 +10,7 @@ data class InstitutionSearchDoc(
val name: LanguageContainer,
// Facettes
val canton: LanguageContainer,
val canton: List<LanguageContainer>,
val type: List<LanguageContainer>,
val documentType: List<LanguageContainer>,
......
......@@ -19,6 +19,7 @@
package org.memobase.model
import com.fasterxml.jackson.annotation.JsonInclude
import org.memobase.helpers.KEYS
@JsonInclude(JsonInclude.Include.NON_NULL)
data class LanguageContainer(
......@@ -27,6 +28,25 @@ data class LanguageContainer(
val it: List<String>,
val un: List<String> // if the language is not known
) {
companion object {
val EMPTY = LanguageContainer(
emptyList(),
emptyList(),
emptyList(),
emptyList()
)
val DEFAULT = LanguageContainer(
listOf(KEYS.missingLabelDe),
listOf(KEYS.missingLabelFr),
listOf(KEYS.missingLabelIt),
listOf(KEYS.missingLabelEn)
)
fun placeholder(placeholder: String): LanguageContainer {
return LanguageContainer(listOf(placeholder), listOf(placeholder), listOf(placeholder), listOf(placeholder))
}
}
fun toList(): List<String> {
return de + fr + it + un
}
......
......@@ -18,6 +18,7 @@
package org.memobase.model
import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonInclude
@JsonInclude(JsonInclude.Include.NON_EMPTY)
......@@ -30,7 +31,8 @@ data class SearchDoc(
// Datatype properties
val type: String,
val sourceID: String,
val recordId: String,
@JsonIgnore
private val recordId: String,
val abstract: List<LanguageContainer>,
val descriptiveNote: List<LanguageContainer>,
val scopeAndContent: List<LanguageContainer>,
......
app:
media:
url: ${MEDIA_SERVER_URL:?system}
institutionTypeLabelsPath: "/configs/institution_types/labels.csv"
kafka:
streams:
bootstrap.servers: ${KAFKA_BOOTSTRAP_SERVERS:?system}
......
......@@ -17,6 +17,7 @@
*/
package org.memobase
import com.beust.klaxon.Klaxon
import org.apache.kafka.common.serialization.StringDeserializer
import org.apache.kafka.common.serialization.StringSerializer
import org.apache.kafka.streams.TopologyTestDriver
......@@ -28,6 +29,7 @@ import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.assertAll
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
import org.memobase.model.Report
import org.memobase.params.TestParam
import java.io.File
import java.nio.charset.Charset
......@@ -42,6 +44,8 @@ class IntegrationTest {
return File("$resourcePath/$fileName").readText(Charset.defaultCharset())
}
private val klaxon = Klaxon()
@ParameterizedTest
@MethodSource("testParams")
fun `integration tests`(params: TestParam) {
......@@ -96,7 +100,9 @@ class IntegrationTest {
)
testDriver.pipeInput(
factory.create(
service.settings.inputTopic, "https://memobase.ch/institution/RadioX", readFile("institution/input.json")
service.settings.inputTopic,
"https://memobase.ch/institution/RadioX",
readFile("institution/input.json")
)
)
......@@ -113,7 +119,7 @@ class IntegrationTest {
)
val reportKey = report.key()
val reportValue = report.value()
val reportValue = klaxon.parse<Report>(report.value())!!
val value = record.value()
val key = record.key()
......@@ -123,7 +129,17 @@ class IntegrationTest {
assertThat(value)
.isEqualTo(readFile("institution/output.json"))
},
{ assertThat(key).isEqualTo("https://memobase.ch/institution/RadioX") }
{ assertThat(key).isEqualTo("https://memobase.ch/institution/RadioX") },
{ assertThat(reportKey).isEqualTo("https://memobase.ch/institution/RadioX") },
{
assertThat(reportValue).isEqualTo(
Report(
"https://memobase.ch/institution/RadioX",
"SUCCESS",
"Transformed message into search doc."
)
)
}
)
}
}
......@@ -29,6 +29,7 @@ import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
import org.memobase.builders.PersonFacetBuilder
import org.memobase.helpers.DateFacetBuildHelpers
import org.memobase.helpers.KEYS
import org.memobase.params.PersonFacetBuilderParams
import org.memobase.params.TestDate
import org.memobase.rdf.NS
......
id,de,fr,it │
Q166118,Archiv,Archives,Archivio │
Q327333,Behörde,autorité,autorità │
Q7075,Bibliothek,Bibliothèque,Biblioteca │
Q2385804,Bildungseinrichtung,Institution d'enseignement,Istituzione d'istruzione │
Q31855,Forschungseinrichtung,Institution de recherche,Istituzione di ricerca │
Q38723,Hochschule,Université,Università │
Q33506,Museum,Musée,Museo │
Q163740,Non-Profit-Organisation,Organisme sans but lucratif,Organizzazione non a scopo di lucro │
Q591763,Privatwirtschaft,Secteur privé,Settore privato │
Q15265344,Rundfunkveranstalter,Radio- et télédiffuseur,Emittente │
Q15911314,Verband,Association,Associazione │
Q2029941,Veranstalter,Organisateur,Organizzatore
\ 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":["Bern: Altstadt, obere; Bundesplatz 4; Bärenplatz (31); Käfiggässchen; -- Fuhrwerk; Automobil; Lastwagen; Transport, Verkehr; Strassenbeleuchtung"]}],"type":"Foto","sourceID":"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"]},"id":"https://memobase.ch/record/Burgerbib-Krebser-216133"}
\ 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
{"title":[{"de":[],"fr":[],"it":[],"un":["Anonym -- Armut"]}],"type":"Foto","sourceID":"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"]},"id":"https://memobase.ch/record/Burgerbib-Krebser-208576"}
\ No newline at end of file
{"title":[{"de":[],"fr":[],"it":[],"un":["Tschu Tschu"]}],"type":"Film","sourceID":"162354","id":"https://memobase.ch/record/LS-film-162354","abstract":[{"de":[],"fr":[],"it":[],"un":["Dokumentation über die Marzili Bahn, Bern. <br><br>Beinhaltet Cartoon über die Geschichte der Eisenbahn, die verschiedenen Schienen - und Antriebssysteme sowie über die Gründungsgeschichte der Marzili Bahn.<br><br>Details<br>Cartoon: Schriftzug Tschu Tschu, Variationen zum Thema Eisenbahn, Ein Film von Werner Emmisberger, Geschichte der Eisenbahn: 1 Eisenbahn von Georges Steffenson. <br>Beispiele verschiedener Systeme und Probleme: Absurdes, Utopisches und Skuriles.<br>Wasserangetriebenes System der Marzilibahn: mit Wasser gefüllter Bergwagen zieht den leeren Talwagen durch sein Gewicht während der Abfahrt hoch.<br>1884 Gründung des Initiativkommitees der Marzili Bahn AG, Gründungsmitglieder: Rothenbach, Gfeller, Lutzmüller, Morgenthaler, Münger, Salvisberg und Schnydermuggli. <br>Ab 1885, 1 Drahtseilbahn Europas: Marzili Bahn AG, Höhendifferenz 30m, Länge 100m. <br>Ab 1974 kein Wasserantrieb mehr, neue Wagen, elektrisch angetrieben. <br>Div. Ansichten Marzilibahn Aussen und Innen: alte Wagen, neue Wagen, Fahrt, Passagiere, Bahnpersonal, Berg- und Talstation. Longstreet Jazz Band in der Marzilibahn."]}],"rightsHolder":[{"de":[],"fr":[],"it":[],"un":["Lichtspiel / Kinemathek Bern"]}],"sameAs":["http://xray876.server4you.net/suche.lichtspiel/mediaplayer/videoportal_detail.aspx?FilmWorkContentID=162354"],"genre":[{"name":{"de":[],"fr":[],"it":[],"un":["Dokumentar"]},"facet":["Dokumentar"]},{"name":{"de":[],"fr":[],"it":["Documentazione"],"un":[]},"facet":["Documentazione"]}],"keywords":[{"name":{"de":[],"fr":[],"it":[],"un":["Eisenbahn"]},"facet":["Eisenbahn"]},{"name":{"de":[],"fr":[],"it":[],"un":["Seilbahn"]},"facet":["Seilbahn"]}],"languageContent":[{"de":[],"fr":[],"it":[],"un":["deu"]},{"de":[],"fr":[],"it":["tedesco"],"un":[]}],"language":[{"name":{"de":[],"fr":[],"it":[],"un":["deu"]},"facet":["deu"]},{"name":{"de":[],"fr":[],"it":["tedesco"],"un":[]},"facet":["tedesco"]}],"personCreator":[{"name":{"de":[],"fr":[],"it":[],"un":["Werner Emmisberger (Regie)"]},"relation":{"de":[],"fr":[],"it":[],"un":["Author"]},"filter":"(Regie), Werner Emmisberger","facet":["0~R~","1~R~(Regie), Werner Emmisberger~"]}],"personContributor":[{"name":{"de":[],"fr":[],"it":[],"un":["Longstreet Jazz Band"]},"relation":{"de":[],"fr":[],"it":[],"un":["Interpret"]},"filter":"Band, Longstreet Jazz","facet":["0~B~","1~B~Band, Longstreet Jazz~"]}],"personsFacet":["0~B~","1~B~Band, Longstreet Jazz~","0~R~","1~R~(Regie), Werner Emmisberger~"],"placeRelated":[{"name":{"de":[],"fr":[],"it":[],"un":["SZ"]},"filter":"SZ","facet":["0~S~","1~S~SZ~"]}],"placeFacet":["0~S~","1~S~SZ~"],"dateCreated":[{"date":"1973","sort":"1973","facet":["0~20.Jahrhundert~","1~20.Jahrhundert~1971-1980~"]}],"institution":[{"name":{"de":[],"fr":[],"it":[],"un":[]},"facet":["LS"]}],"recordSet":{"name":{"de":[],"fr":[],"it":[],"un":[]},"facet":["LS-film"]},"memoriavClaim":true,"locator":"https://media.memobase.k8s.unibas.ch/memo/LS-film-162354-1","accessDigital":["public"],"digital":{},"durationPhysical":["00:11:00"],"accessPhysical":["onsite"],"physicalCharacteristics":[{"de":[],"fr":[],"it":[],"un":["Verfahren: Lä