Commit 448c7a31 authored by Jonas Waeber's avatar Jonas Waeber
Browse files

Update basics

parent 8fd39835
...@@ -4,21 +4,23 @@ Transforms json objects into RDF Resources. ...@@ -4,21 +4,23 @@ Transforms json objects into RDF Resources.
### Canton Query ### Canton Query
Here the codes were added manually and the names adjusted to exclude the word canton. Just kept the name.
https://query.wikidata.org/ https://query.wikidata.org/
```sparql ```sparql
SELECT ?item ?labelDE ?labelFR ?labelIT SELECT ?item ?de ?fr ?it
WHERE WHERE
{ {
?item wdt:P31 wd:Q23058. ?item wdt:P31 wd:Q23058.
?item rdfs:label ?labelDE . ?item rdfs:label ?de .
FILTER(LANG(?labelDE) = "de") FILTER(LANG(?de) = "de")
?item rdfs:label ?labelFR . ?item rdfs:label ?fr .
FILTER(LANG(?labelFR) = "fr") FILTER(LANG(?fr) = "fr")
?item rdfs:label ?labelIT . ?item rdfs:label ?it .
FILTER(LANG(?labelIT) = "fr") FILTER(LANG(?it) = "it")
} }
``` ```
...@@ -26,22 +28,27 @@ WHERE ...@@ -26,22 +28,27 @@ WHERE
https://query.wikidata.org/ https://query.wikidata.org/
```sql ```sparql
SELECT ?item (GROUP_CONCAT(DISTINCT ?postalCode; SEPARATOR=",") as ?groupedPostalCode) ?canton (GROUP_CONCAT(DISTINCT ?coordinates; SEPARATOR=",") as ?groupedCoordinates) SELECT (GROUP_CONCAT(DISTINCT ?postalCode; SEPARATOR=",") as ?code) ?id (SAMPLE(?de) as ?de) (SAMPLE(?fr) as ?fr) (SAMPLE(?it) as ?it)
WHERE WHERE
{ {
?item wdt:P31 wd:Q70208. # swiss municipalities ?id wdt:P31 wd:Q70208. # swiss municipalities
?item wdt:P281 ?postalCode . ?id wdt:P281 ?postalCode .
?item wdt:P625 ?coordinates .
?item wdt:P131 ?canton . ?id rdfs:label ?de .
?canton wdt:P31 wd:Q23058 . # adminUnit is canton of switzerland FILTER(LANG(?de) = "de")
?id rdfs:label ?fr .
FILTER(LANG(?fr) = "fr")
?id rdfs:label ?it .
FILTER(LANG(?it) = "it")
MINUS { MINUS {
?item wdt:P31 wd:Q685309 # removes former municipalities ?id wdt:P31 wd:Q685309 # removes former municipalities
} }
}
} GROUP BY ?id
GROUP BY ?item ?canton
``` ```
### More Complete Query ### More Complete Query
......
...@@ -86,10 +86,10 @@ test { ...@@ -86,10 +86,10 @@ test {
sourceSets { sourceSets {
main.kotlin.srcDirs += 'src/main/kotlin' main.kotlin.srcDirs += 'src/main/kotlin'
main.resources.srcDirs = [ "src/main/resources" ] main.resources.srcDirs = [ "src/main/resources" ]
main.resources.includes = [ "**/*.yml", "**/*.xml", "**/*.tsv"] main.resources.includes = [ "**/*.yml", "**/*.xml", "**/*.tsv", "**/*.csv"]
test.kotlin.srcDirs += 'src/test/kotlin' test.kotlin.srcDirs += 'src/test/kotlin'
test.resources.srcDirs = [ "src/test/resources" ] test.resources.srcDirs = [ "src/test/resources" ]
test.resources.includes = [ "**/*.yml", "**/*.xml", "**/*.tsv"] test.resources.includes = [ "**/*.yml", "**/*.xml", "**/*.tsv", "**/*.csv"]
} }
plugins.withType(DistributionPlugin) { plugins.withType(DistributionPlugin) {
......
deploymentName: drupal-sync
inputTopic: drupal-json-api-input
outputTopic: drupal-sync-output
kafkaConfigs: prod-kafka-bootstrap-servers
\ No newline at end of file
...@@ -17,10 +17,10 @@ ...@@ -17,10 +17,10 @@
*/ */
package org.memobase package org.memobase
import kotlin.system.exitProcess
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import org.memobase.model.Institution import org.memobase.model.Canton
import org.memobase.model.Municipality import org.memobase.model.Municipality
import kotlin.system.exitProcess
object Helpers { object Helpers {
...@@ -30,22 +30,19 @@ object Helpers { ...@@ -30,22 +30,19 @@ object Helpers {
val stream = ClassLoader.getSystemResourceAsStream("municipalities.tsv") val stream = ClassLoader.getSystemResourceAsStream("municipalities.tsv")
if (stream != null) { if (stream != null) {
return stream.bufferedReader().lineSequence().filterNot { return stream.bufferedReader().lineSequence().filterNot {
it.startsWith("item") it.startsWith("code")
}.map { }.map {
val values = it.split("\t") val values = it.split("\t")
Municipality( val codes = values[0].split(",").map { code -> code.trim() }
values[1].trim('"') codes.flatMap { code ->
.split(",") code.split("-").map { c -> c.trim() }
.map { code -> code.trim() } }.map { code ->
.flatMap { code -> code.split("-") } Pair(code, Municipality(
.map { code -> code.trim() }, values[1].trim(),
values[2].trim('<', '>'), values[2].trim(),
values[0].trim('<', '>'), values[3].trim(),
values[3].split(",") values[4].trim()
) ))
}.map { municipality ->
municipality.postalCodes.map { code ->
Pair(code, municipality)
} }
}.flatten().toMap() }.flatten().toMap()
} else { } else {
...@@ -53,4 +50,20 @@ object Helpers { ...@@ -53,4 +50,20 @@ object Helpers {
exitProcess(1) exitProcess(1)
} }
} }
fun getCantons(): Map<String, Canton> {
val stream = ClassLoader.getSystemResourceAsStream("cantons.csv")
if (stream != null) {
return stream.bufferedReader().lineSequence().filterNot {
it.startsWith("code")
}.map {
val values = it.split(",")
Pair(values[0].trim(), Canton(values[1].trim(), values[2].trim(), values[3].trim(), values[4].trim()))
}.toMap()
} else {
log.error("Could not load cantons.csv from classpath!")
exitProcess(1)
}
}
} }
\ No newline at end of file
...@@ -31,9 +31,7 @@ import java.io.StringReader ...@@ -31,9 +31,7 @@ import java.io.StringReader
class KafkaTopology(private val settings: SettingsLoader) { class KafkaTopology(private val settings: SettingsLoader) {
private val log = LogManager.getLogger("StreamsProcessing") private val log = LogManager.getLogger("StreamsProcessing")
private val transformer = Transform()
private val municipalities = Helpers.getMunicipalities()
private val transformer = Transform(municipalities)
fun build(): Topology { fun build(): Topology {
val builder = StreamsBuilder() val builder = StreamsBuilder()
......
...@@ -23,15 +23,21 @@ import org.apache.jena.riot.RDFDataMgr ...@@ -23,15 +23,21 @@ import org.apache.jena.riot.RDFDataMgr
import org.apache.jena.riot.RDFFormat import org.apache.jena.riot.RDFFormat
import org.apache.kafka.streams.KeyValue import org.apache.kafka.streams.KeyValue
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import org.memobase.model.* import org.memobase.model.Municipality
import org.memobase.rdf.NS import org.memobase.rdf.NS
import org.memobase.rdf.RDF import org.memobase.rdf.RDF
import org.memobase.rdf.RICO import org.memobase.rdf.RICO
import org.memobase.rdf.WD import rdf.WD
import java.io.StringWriter import java.io.StringWriter
import org.memobase.model.Address
import org.memobase.model.Institution
import org.memobase.model.RecordSet
import org.memobase.rdf.SCHEMA
class Transform(private val municipalities: Map<String, Municipality>) { class Transform {
private val log = LogManager.getLogger("Transform") private val log = LogManager.getLogger("InstitutionTransform")
private val cantons = Helpers.getCantons()
private val municipalities = Helpers.getMunicipalities()
fun createInstitution(input: Institution): Pair<String, Model> { fun createInstitution(input: Institution): Pair<String, Model> {
...@@ -45,7 +51,12 @@ class Transform(private val municipalities: Map<String, Municipality>) { ...@@ -45,7 +51,12 @@ class Transform(private val municipalities: Map<String, Municipality>) {
identifier.addProperty(RICO.identifier, literal(input.field_memobase_id)) identifier.addProperty(RICO.identifier, literal(input.field_memobase_id))
resource.addProperty(RICO.identifiedBy, identifier) resource.addProperty(RICO.identifiedBy, identifier)
// TODO: proper multi language integration! val oldIdentifier = model.createResource()
oldIdentifier.addProperty(RDF.type, RICO.Identifier)
oldIdentifier.addProperty(RICO.type, literal("oldMemobase"))
oldIdentifier.addProperty(RICO.identifier, literal(input.field_old_memobase_id))
resource.addProperty(RICO.identifiedBy, identifier)
resource.addProperty(RICO.name, langLiteral(input.title, "de")) resource.addProperty(RICO.name, langLiteral(input.title, "de"))
resource.addProperty(RICO.name, langLiteral(input.title_fr, "fr")) resource.addProperty(RICO.name, langLiteral(input.title_fr, "fr"))
resource.addProperty(RICO.name, langLiteral(input.title_it, "it")) resource.addProperty(RICO.name, langLiteral(input.title_it, "it"))
...@@ -76,6 +87,9 @@ class Transform(private val municipalities: Map<String, Municipality>) { ...@@ -76,6 +87,9 @@ class Transform(private val municipalities: Map<String, Municipality>) {
if (it != null) if (it != null)
resource.addProperty(WD.onlineArchive, literal(it.uri)) resource.addProperty(WD.onlineArchive, literal(it.uri))
} }
input.field_institution_types.forEach {
resource.addProperty(WD.typeOfInstitution, model.createResource(NS.wd + it.substringAfterLast("/")))
}
return Pair(resource.uri, model) return Pair(resource.uri, model)
} }
...@@ -101,24 +115,40 @@ class Transform(private val municipalities: Map<String, Municipality>) { ...@@ -101,24 +115,40 @@ class Transform(private val municipalities: Map<String, Municipality>) {
val postalCode = address.postal_code.trim() val postalCode = address.postal_code.trim()
val municipality = if (municipalities.containsKey(postalCode)) {
municipalities[postalCode]
} else {
// the input validation in drupal should ensure that this never happens.
log.error("Invalid postal code: $postalCode")
null
}
location.addProperty(RDF.type, RICO.Place) location.addProperty(RDF.type, RICO.Place)
location.addProperty(WD.postalCode, literal(postalCode)) location.addProperty(WD.postalCode, literal(postalCode))
location.addProperty(WD.coordinates, literal(address.coordinates))
// does not enrich city, canton or cantons, if the postal code is not in the list. // does not enrich city, canton or cantons, if the postal code is not in the list.
if (municipality != null) {
// canton val canton = ResourceFactory.createResource()
location.addProperty(WD.adminUnit, ResourceFactory.createResource(municipality.canton)) canton.addProperty(RDF.type, RICO.Place)
// city location.addProperty(WD.adminUnit, canton)
location.addProperty(WD.adminUnit, ResourceFactory.createResource(municipality.id)) cantons[address.administrative_area].let {
// TODO: Replace this with the coordinates from source! // this should always be the case!
municipality.coordinates.forEach { coordinate -> if (it != null) {
location.addProperty(WD.coordinates, literal(coordinate)) canton.addProperty(RICO.name, langLiteral(it.de, "de"))
canton.addProperty(RICO.name, langLiteral(it.fr, "fr"))
canton.addProperty(RICO.name, langLiteral(it.it, "it"))
canton.addProperty(SCHEMA.sameAs, it.id)
} else {
// this shouldn't happen!
canton.addProperty(RICO.name, literal("Unknown"))
}
}
val municipality = ResourceFactory.createResource()
municipality.addProperty(RDF.type, RICO.Place)
location.addProperty(WD.adminUnit, municipality)
municipalities[postalCode].let {
if (it != null) {
municipality.addProperty(RICO.name, langLiteral(it.de, "de"))
municipality.addProperty(RICO.name, langLiteral(it.fr, "fr"))
municipality.addProperty(RICO.name, langLiteral(it.it, "it"))
municipality.addProperty(SCHEMA.sameAs, it.id)
} else {
municipality.addProperty(RICO.name, literal(address.locality))
} }
} }
// country is currently hard coded to switzerland! // country is currently hard coded to switzerland!
......
package org.memobase.model package org.memobase.model
data class Address( data class Address(
val langcode: String?,
val country_code: String, val country_code: String,
val address_line1: String, val administrative_area: String,
val address_line2: String?,
val locality: String, val locality: String,
val postal_code: String,
val administrative_area: String?,
val dependent_locality: String?, val dependent_locality: String?,
val langcode: String?, val postal_code: String,
val sorting_code: String?, val sorting_code: String?,
val address_line1: String,
val address_line2: String?,
val organization: String?, val organization: String?,
val given_name: String?, val given_name: String?,
val additional_name: String?, val additional_name: String?,
val family_name: String? val family_name: String?,
val coordinates: String
) )
\ No newline at end of file
/*
* Drupal Sync 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.model
data class Canton(
val id: String,
val de: String,
val fr: String,
val it: String
)
...@@ -25,13 +25,19 @@ data class Institution( ...@@ -25,13 +25,19 @@ data class Institution(
val title_it: String, val title_it: String,
val field_address: List<Address>, val field_address: List<Address>,
val field_isil: String?, val field_isil: String?,
val field_memobase_id: String, val field_memobase_id: String,
val field_old_memobase_id: String,
val field_email: String?, val field_email: String?,
val field_link_archive_catalog: Link?,
val field_website: Link?, val field_website: Link?,
val field_link_archive_catalog: Link?,
val field_text: RichText, val field_text: RichText,
val field_text_fr: RichText, val field_text_fr: RichText,
val field_text_it: RichText val field_text_it: RichText,
val field_institution_types: List<String>,
val field_wikidata_id: Link
) : Input("Institution") ) : Input("Institution")
\ No newline at end of file
...@@ -18,8 +18,8 @@ ...@@ -18,8 +18,8 @@
package org.memobase.model package org.memobase.model
data class Municipality( data class Municipality(
val postalCodes: List<String>,
val canton: String,
val id: String, val id: String,
val coordinates: List<String> val de: String,
val fr: String,
val it: String
) )
\ No newline at end of file
package org.memobase.rdf
import org.apache.jena.rdf.model.Property
import org.apache.jena.rdf.model.Resource
import org.apache.jena.rdf.model.ResourceFactory
object WD {
val location = res("Q17334923")
val switzerland = res("Q39")
val isil = prop("P791")
val street = prop("P669")
val streetNumber = prop("P670")
val streetAddress = prop("P6375")
val adminUnit = prop("P131")
val postalCode = prop("P281")
val country = prop("P17")
val website = prop("P856")
val emailAddress = prop("P968")
val onlineArchive = prop("P2699")
val coordinates = prop("P625")
val image = prop("P18")
val logo = prop("P154")
val typeOfInstitution = prop("P31")
private fun prop(name: String): Property {
return ResourceFactory.createProperty(NS.wdt, name)
}
fun res(name: String): Resource {
return ResourceFactory.createResource(NS.wd + name)
}
}
\ No newline at end of file
item,labelDE,labelFR,labelIT code,item,de,fr,it
http://www.wikidata.org/entity/Q834,Kanton Wallis,canton du Valais,canton du Valais VS,http://www.wikidata.org/entity/Q834,Wallis,Valais,Valais
http://www.wikidata.org/entity/Q11922,Kanton Glarus,canton de Glaris,canton de Glaris GL,http://www.wikidata.org/entity/Q11922,Glarus,Glaris,Glaris
http://www.wikidata.org/entity/Q11911,Kanton Bern,canton de Berne,canton de Berne BE,http://www.wikidata.org/entity/Q11911,Bern,Berne,Berne
http://www.wikidata.org/entity/Q11929,Kanton Solothurn,canton de Soleure,canton de Soleure SO,http://www.wikidata.org/entity/Q11929,Solothurn,Soleure,Soleure
http://www.wikidata.org/entity/Q11917,Kanton Genf,canton de Genève,canton de Genève GE,http://www.wikidata.org/entity/Q11917,Genf,Genève,Genève
http://www.wikidata.org/entity/Q11925,Kanton Graubünden,canton des Grisons,canton des Grisons GR,http://www.wikidata.org/entity/Q11925,Graubünden,Grisons,Grisons
http://www.wikidata.org/entity/Q11933,Kanton Zug,canton de Zoug,canton de Zoug ZG,http://www.wikidata.org/entity/Q11933,Zug,Zoug,Zoug
http://www.wikidata.org/entity/Q11943,Kanton Zürich,canton de Zurich,canton de Zurich ZH,http://www.wikidata.org/entity/Q11943,Zürich,Zurich,Zurich
http://www.wikidata.org/entity/Q11972,Kanton Aargau,canton d'Argovie,canton d'Argovie AG,http://www.wikidata.org/entity/Q11972,Aargau,Argovie,Argovie
http://www.wikidata.org/entity/Q12079,Kanton Appenzell Ausserrhoden,Appenzell Rhodes-Extérieures,Appenzell Rhodes-Extérieures AR,http://www.wikidata.org/entity/Q12079,Appenzell Ausserrhoden,Appenzell Rhodes-Extérieures,Appenzell Rhodes-Extérieures
http://www.wikidata.org/entity/Q12121,Kanton Luzern,canton de Lucerne,canton de Lucerne LU,http://www.wikidata.org/entity/Q12121,Luzern,Lucerne,Lucerne
http://www.wikidata.org/entity/Q12146,Kanton Basel-Landschaft,canton de Bâle-Campagne,canton de Bâle-Campagne BL,http://www.wikidata.org/entity/Q12146,Basel-Landschaft,Bâle-Campagne,Bâle-Campagne
http://www.wikidata.org/entity/Q12094,Kanton Appenzell Innerrhoden,canton de Appenzell Rhodes-Intérieures,canton de Appenzell Rhodes-Intérieures AI,http://www.wikidata.org/entity/Q12094,Appenzell Innerrhoden,Appenzell Rhodes-Intérieures,Appenzell Rhodes-Intérieures
http://www.wikidata.org/entity/Q12172,Kanton Basel-Stadt,canton de Bâle-Ville,canton de Bâle-Ville BS,http://www.wikidata.org/entity/Q12172,Basel-Stadt,Bâle-Ville,Bâle-Ville
http://www.wikidata.org/entity/Q12404,Kanton Uri,canton d'Uri,canton d'Uri UR,http://www.wikidata.org/entity/Q12404,Uri,Uri,Uri
http://www.wikidata.org/entity/Q12592,Kanton Nidwalden,canton de Nidwald,canton de Nidwald NW,http://www.wikidata.org/entity/Q12592,Nidwalden,Nidwald,Nidwald
http://www.wikidata.org/entity/Q12713,Kanton Thurgau,canton de Thurgovie,canton de Thurgovie TG,http://www.wikidata.org/entity/Q12713,Thurgau,Thurgovie,Thurgovie
http://www.wikidata.org/entity/Q12724,Kanton Tessin,canton du Tessin,canton du Tessin TI,http://www.wikidata.org/entity/Q12724,Tessin,Tessin,Tessin
http://www.wikidata.org/entity/Q12640,Kanton Freiburg,canton de Fribourg,canton de Fribourg FR,http://www.wikidata.org/entity/Q12640,Freiburg,Fribourg,Fribourg
http://www.wikidata.org/entity/Q12573,Kanton Obwalden,canton d'Obwald,canton d'Obwald OW,http://www.wikidata.org/entity/Q12573,Obwalden,Obwald,Obwald
http://www.wikidata.org/entity/Q12433,Kanton Schwyz,canton de Schwytz,canton de Schwytz SZ,http://www.wikidata.org/entity/Q12433,Schwyz,Schwytz,Schwytz
http://www.wikidata.org/entity/Q12746,Kanton St. Gallen,canton de Saint-Gall,canton de Saint-Gall SG,http://www.wikidata.org/entity/Q12746,St. Gallen,Saint-Gall,Saint-Gall
http://www.wikidata.org/entity/Q12697,Kanton Schaffhausen,canton de Schaffhouse,canton de Schaffhouse SH,http://www.wikidata.org/entity/Q12697,Schaffhausen,Schaffhouse,Schaffhouse
http://www.wikidata.org/entity/Q12738,Kanton Neuenburg,canton de Neuchâtel,canton de Neuchâtel NE,http://www.wikidata.org/entity/Q12738,Neuenburg,Neuchâtel,Neuchâtel
http://www.wikidata.org/entity/Q12755,Kanton Jura,canton du Jura,canton du Jura JU,http://www.wikidata.org/entity/Q12755,Jura,Jura,Jura
http://www.wikidata.org/entity/Q12771,Kanton Waadt,canton de Vaud,canton de Vaud VD,http://www.wikidata.org/entity/Q12771,Waadt,Vaud,Vaud
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -42,7 +42,13 @@ class Test { ...@@ -42,7 +42,13 @@ class Test {
fun `test municipalities loader`() { fun `test municipalities loader`() {
val result = Helpers.getMunicipalities() val result = Helpers.getMunicipalities()
assertThat(result) assertThat(result)
.isNotNull .isNotEmpty
}
@Test
fun `test cantons loader`() {
val result = Helpers.getCantons()
assertThat(result)
.isNotEmpty .isNotEmpty
} }
......
code,item,de,fr,it
VS,http://www.wikidata.org/entity/Q834,Wallis,Valais,Vallese
GL,http://www.wikidata.org/entity/Q11922,Glarus,Glaris,Glarona
BE,http://www.wikidata.org/entity/Q11911,Bern,Berne,Berna
SO,http://www.wikidata.org/entity/Q11929,Solothurn,Soleure,Soletta
GE,http://www.wikidata.org/entity/Q11917,Genf,Genève,Ginevra
GR,http://www.wikidata.org/entity/Q11925,Graubünden,Grisons,Grigioni
ZG,http://www.wikidata.org/entity/Q11933,Zug,Zoug,Zugo
ZH,http://www.wikidata.org/entity/Q11943,Zürich,Zurich,Zurigo
AG,http://www.wikidata.org/entity/Q11972,Aargau,Argovie,Argovia
AR,http://www.wikidata.org/entity/Q12079,Appenzell Ausserrhoden,Appenzell Rhodes-Extérieures,Appenzello Esterno
LU,http://www.wikidata.org/entity/Q12121,Luzern,Lucerne,Lucerna
BL,http://www.wikidata.org/entity/Q12146,Basel-Landschaft,Bâle-Campagne,Basilea Campagna
AI,http://www.wikidata.org/entity/Q12094,Appenzell Innerrhoden,Appenzell Rhodes-Intérieures,Appenzello Interno
BS,http://www.wikidata.org/entity/Q12172,Basel-Stadt,Bâle-Ville,Basilea Città
UR,http://www.wikidata.org/entity/Q12404,Uri,Uri,Uri
NW,http://www.wikidata.org/entity/Q12592,Nidwalden,Nidwald,Nidvaldo
TG,http://www.wikidata.org/entity/Q12713,Thurgau,Thurgovie,Turgovia
TI,http://www.wikidata.org/entity/Q12724,Tessin,Tessin,Ticino
FR,http://www.wikidata.org/entity/Q12640,Freiburg,Fribourg,Friburgo
OW,http://www.wikidata.org/entity/Q12573,Obwalden,Obwald,Obvaldo
SZ,http://www.wikidata.org/entity/Q12433,Schwyz,Schwytz,Svitto
SG,http://www.wikidata.org/entity/Q12746,St. Gallen,Saint-Gall,San Gallo
SH,http://www.wikidata.org/entity/Q12697,Schaffhausen,Schaffhouse,Sciaffusa
NE,http://www.wikidata.org/entity/Q12738,Neuenburg,Neuchâtel,Neuchâtel
JU,http://www.wikidata.org/entity/Q12755,Jura,Jura,Giura
VD,http://www.wikidata.org/entity/Q12771,Waadt,Vaud,Vaud
{ {
"type": "node--institution", "type": "node--institution",
"status": true, "status": true,
"title": "Staatsarchiv des Kantons Aargau", "title": "Test institution Oana",
"title_fr": "Archives de l’Etat Argovie", "title_fr": "Test institution Oana (FR)",
"title_it": "Archivio cantonale del Argovia", "title_it": "Test institution Oana (IT)",
"field_address": [