From 0626fd256212f4fb5e3cc19a798c786f3c281125 Mon Sep 17 00:00:00 2001
From: Pierre Choffet <peuc@wanadoo.fr>
Date: Thu, 30 Dec 2021 12:11:38 -0500
Subject: [PATCH] Add WMO demonstration tools

---
 demo.sh                               |  82 +++++++++
 import.sh                             | 149 ++++++++++++++++
 xslts/canonicalize.xslt               |  43 +++++
 xslts/get_next_step.xslt              | 244 ++++++++++++++++++++++++++
 xslts/isolate_station.xslt            |  45 +++++
 xslts/remove_labels_descriptions.xslt |  37 ++++
 xslts/replace_id.xslt                 |  59 +++++++
 7 files changed, 659 insertions(+)
 create mode 100755 demo.sh
 create mode 100755 import.sh
 create mode 100644 xslts/canonicalize.xslt
 create mode 100644 xslts/get_next_step.xslt
 create mode 100644 xslts/isolate_station.xslt
 create mode 100644 xslts/remove_labels_descriptions.xslt
 create mode 100644 xslts/replace_id.xslt

diff --git a/demo.sh b/demo.sh
new file mode 100755
index 0000000..c708db5
--- /dev/null
+++ b/demo.sh
@@ -0,0 +1,82 @@
+#!/bin/bash
+
+# demo.sh - Process demonstration for WMO.
+# Copyright (C) 2021  Pierre Choffet
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of version 3 of the GNU General Public License as published
+# by the Free Software Foundation.
+#
+# 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+set -euo pipefail
+
+function usage {
+	cat << "EOF"
+Import OSCAR station metadata to Wikidata
+
+USAGE: intro.sh <wdef_file> <station_id>
+EOF
+}
+
+function intro {
+	cat >&2 << "EOF"
+
+   .g8""8q.    .M"""bgd   .g8"""bgd     db      `7MM"""Mq.        mm            
+ .dP'    `YM. ,MI    "Y .dP'     `M    ;MM:       MM   `MM.       MM            
+ dM'      `MM `MMb.     dM'       `   ,V^MM.      MM   ,M9      mmMMmm ,pW"Wq.  
+ MM        MM   `YMMNq. MM           ,M  `MM      MMmmdM9         MM  6W'   `Wb 
+ MM.      ,MP .     `MM MM.          AbmmmqMA     MM  YM.         MM  8M     M8 
+ `Mb.    ,dP' Mb     dM `Mb.     ,' A'     VML    MM   `Mb.       MM  YA.   ,A9 
+   `"bmmd"'   P"Ybmmd"    `"bmmmd'.AMA.   .AMMA..JMML. .JMM.      `Mbmo`Ybmd9'  
+                                                                                
+                       ,,              ,,        ,,                             
+   `7MMF'     A     `7MF'db  `7MM        db      `7MM           mm                
+     `MA     ,MA     ,V        MM                  MM           MM                
+      VM:   ,VVM:   ,V `7MM    MM  ,MP'`7MM   ,M""bMM   ,6"Yb.mmMMmm  ,6"Yb.      
+       MM.  M' MM.  M'   MM    MM ;Y     MM ,AP    MM  8)   MM  MM   8)   MM      
+       `MM A'  `MM A'    MM    MM;Mm     MM 8MI    MM   ,pm9MM  MM    ,pm9MM      
+        :MM;    :MM;     MM    MM `Mb.   MM `Mb    MM  8M   MM  MM   8M   MM      
+         VF      VF    .JMML..JMML. YA..JMML.`Wbmd"MML.`Moo9^Yo.`Mbmo`Moo9^Yo.    
+
+
+This tool has been written in 2021 as a one-time proof of concept to import
+Oscar station metadata into Wikidata. It's not meant to be used outside of this
+controlled environment as it would inevitably lead any careful user to
+insert duplicates and wrong information into the Wikimedia project.
+
+The full original code remains available here for educational purpose.
+
+For up to date code, see the repository at
+<https://git.choffet.net/?p=wmo_to_wikidata.git>
+EOF
+}
+
+# Check user args
+if [ "$#" -ne 2 ]
+then
+	usage
+	exit
+elif [ ! -f "${1}" ]
+then
+	echo "File \"${1}\" doesn't exist." >&2
+	exit
+fi
+
+intro
+exit 1
+
+# Generate partial wdef file
+PARTIAL_WDEF_PATH="$(mktemp)"
+xmlstarlet tr xslts/isolate_station.xslt -s "wigos-id=${2}" "${1}" | xmlstarlet fo > "${PARTIAL_WDEF_PATH}"
+
+# Import it
+./import.sh "${PARTIAL_WDEF_PATH}" elements.log
+rm "${PARTIAL_WDEF_PATH}"
+echo "Import done" >&2
diff --git a/import.sh b/import.sh
new file mode 100755
index 0000000..a0bc477
--- /dev/null
+++ b/import.sh
@@ -0,0 +1,149 @@
+#!/bin/bash
+
+# import.sh - Import data wdef into Wikidata.
+# Copyright (C) 2020-2021  Pierre Choffet
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of version 3 of the GNU General Public License as published
+# by the Free Software Foundation.
+#
+# 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+set -eu
+
+# Internals config
+IMPORT_PLAN_XSLT_PATH='xslts/get_next_step.xslt'
+REPLACE_WDEF_ID_XSLT_PATH='xslts/replace_id.xslt'
+REMOVE_LABELS_DESCRIPTIONS_XSLT_PATH='xslts/remove_labels_descriptions.xslt'
+CANONICALIZE_WDEF_XSLT_PATH='xslts/canonicalize.xslt'
+
+WDEF_PATH="${1}"
+NEW_ELEMENTS_LOG_PATH="${2}"
+
+function createEntity {
+	local -r wdef_id="${1}"
+	local -r entity_json="${2}"
+	
+	echo "→ wd create-entity ${entity_json}" > $(tty)
+	sleep 5
+	
+	# Create entity and get ID afterwards:
+	local -r element_id=$(wd create-entity "${entity_json}" | jq -r .entity.id)
+	
+	echo "${wdef_id} = ${element_id}" >> "${NEW_ELEMENTS_LOG_PATH}"
+	
+	# Replace id
+	local -r new_id_xml=$(mktemp)
+	xmlstarlet tr "${REPLACE_WDEF_ID_XSLT_PATH}" -s old-id="${wdef_id}" -s new-id="${element_id}" "${WDEF_PATH}" > "${new_id_xml}"
+	
+	# Remove labels and descriptions
+	local -r reduced_xml=$(mktemp)
+	xmlstarlet tr "${REMOVE_LABELS_DESCRIPTIONS_XSLT_PATH}" -s wdef-id="${element_id}" "${new_id_xml}" > "${reduced_xml}"
+	rm "${new_id_xml}"
+	
+	echo "${reduced_xml}"
+}
+
+function addClaim {
+	local -r wdef_id="${1}"
+	local -r wd_id="${2}"
+	local -r wd_pid="${3}"
+	local -r value="${4}"
+	
+	echo "→ wd add-claim ${wd_id} ${wd_pid} ${value}" > $(tty)
+	sleep $((3 + $RANDOM % 10))
+	
+	# Create claim and get ID afterwards:
+	local -r claim_id=$(wd add-claim "${wd_id}" "${wd_pid}" "${value}" | jq -r .claim.id)
+	
+	local -r reduced_xml=$(mktemp)
+	xmlstarlet tr "${REPLACE_WDEF_ID_XSLT_PATH}" -s old-id="${wdef_id}" -s new-id="${claim_id}" "${WDEF_PATH}" > "${reduced_xml}"
+	
+	echo "${reduced_xml}"
+}
+
+function addQualifier {
+	local -r qualifier_value_id="${1}"
+	local -r value_id="${2}"
+	local -r wd_pid="${3}"
+	local -r value="${4}"
+	
+	echo "→ wd add-qualifier ${value_id} ${wd_pid} ${value}" > $(tty)
+	sleep $((3 + $RANDOM % 10))
+	
+	# Create qualifier and get ID afterwards:
+	local -r qualifier_id=$(wd add-qualifier "${value_id}" "${wd_pid}" "${value}" | jq -r .claim.qualifiers.${wd_pid}[].hash)
+	
+	local -r reduced_xml=$(mktemp)
+	xmlstarlet tr "${REPLACE_WDEF_ID_XSLT_PATH}" -s old-id="${qualifier_value_id}" -s new-id="${qualifier_id}" "${WDEF_PATH}" > "${reduced_xml}"
+	
+	echo "${reduced_xml}"
+}
+
+# Check user parameters
+if [ ! -s "${1}" ]
+then
+	echo "${1} doesn't exist or is not readable."
+	exit 1
+fi
+
+# Track import generations
+IMPORT_HISTORY_DIR="$(mktemp -d)/"
+GENERATION=1
+
+echo "New elements corresponding IDs will be append in ${NEW_ELEMENTS_LOG_PATH}" >&2
+echo "Import generations log will be in ${IMPORT_HISTORY_DIR}" >&2
+
+# Prepare first step
+cp "${WDEF_PATH}" "${IMPORT_HISTORY_DIR}${GENERATION}.xml"
+WDEF_PATH="${IMPORT_HISTORY_DIR}${GENERATION}.xml"
+
+# Get first step
+NEXT_STEP=$(xmlstarlet tr "${IMPORT_PLAN_XSLT_PATH}" "${WDEF_PATH}" | head -1)
+
+while [ "${NEXT_STEP}" != '' ]
+do
+	WB_COMMAND="${NEXT_STEP%% *}"
+
+	case $WB_COMMAND in
+		create-entity)
+			NEW_XML_PATH=$(createEntity $(echo "${NEXT_STEP}" | cut -d ' ' -f2) "$(echo "${NEXT_STEP}" | cut -d ' ' -f3-)")
+			;;
+		add-claim)
+			NEW_XML_PATH=$(addClaim $(echo "${NEXT_STEP}" | cut -d ' ' -f2) "$(echo "${NEXT_STEP}" | cut -d ' ' -f3)" "$(echo "${NEXT_STEP}" | cut -d ' ' -f4)" "$(echo "${NEXT_STEP}" | cut -d ' ' -f5-)")
+			;;
+		add-claim-no-value)
+			NEW_XML_PATH=$(addClaim $(echo "${NEXT_STEP}" | cut -d ' ' -f2) "$(echo "${NEXT_STEP}" | cut -d ' ' -f3)" "$(echo "${NEXT_STEP}" | cut -d ' ' -f4)" '{"snaktype": "novalue"}')
+			;;
+		add-qualifier)
+			NEW_XML_PATH=$(addQualifier $(echo "${NEXT_STEP}" | cut -d ' ' -f2) "$(echo "${NEXT_STEP}" | cut -d ' ' -f3)" "$(echo "${NEXT_STEP}" | cut -d ' ' -f4)" "$(echo "${NEXT_STEP}" | cut -d ' ' -f5)")
+			;;
+		*)
+			echo "Unexpected \"${WB_COMMAND}\" command."
+			exit 1
+			;;
+	esac
+	
+	# Check returned string is path
+	if [ ! -s "${NEW_XML_PATH}" ]
+	then
+		exit 1
+	fi
+	
+	# Generate new canonical version
+	CANONICALIZED_XML_PATH="$(mktemp)"
+	xmlstarlet tr "${CANONICALIZE_WDEF_XSLT_PATH}" "${NEW_XML_PATH}" > "${CANONICALIZED_XML_PATH}"
+# 	rm "${NEW_XML_PATH}"
+	
+	# Prepare next step
+	GENERATION=$((GENERATION + 1))
+	WDEF_PATH="${IMPORT_HISTORY_DIR}${GENERATION}.xml"
+	mv "${CANONICALIZED_XML_PATH}" "${WDEF_PATH}"
+	NEXT_STEP=$(xmlstarlet tr "${IMPORT_PLAN_XSLT_PATH}" "${WDEF_PATH}" | head -1)
+done
diff --git a/xslts/canonicalize.xslt b/xslts/canonicalize.xslt
new file mode 100644
index 0000000..ae31a64
--- /dev/null
+++ b/xslts/canonicalize.xslt
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- canonicalize.xslt - Rewrite a wdef file into its normal form.
+     Copyright (C) 2021  Pierre Choffet
+
+     This program is free software: you can redistribute it and/or modify
+     it under the terms of version 3 of the GNU General Public License as
+     published by the Free Software Foundation.
+
+     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 General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+     -->
+<xsl:stylesheet version="1.0" xmlns:wdef="https://purl.choffet.net/wdef"
+                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+	<xsl:output indent="yes" method="xml" encoding="utf-8" />
+	<xsl:strip-space elements="*" />
+	
+	<xsl:template match="node()|@*">
+		<xsl:copy>
+			<xsl:apply-templates select="node()|@*" />
+		</xsl:copy>
+	</xsl:template>
+	
+	<!-- wdef:element with no child nodes -->
+	<xsl:template match="wdef:element[not(*)]" />
+	
+	<!-- Local-only wdef:element with no label -->
+	<xsl:template match="wdef:element[substring(@wdef:id, 1, 1) != 'Q' and not(wdef:label)]" />
+	
+	<!-- wdef:property with no child nodes -->
+	<xsl:template match="wdef:property[not(*)]" />
+	
+	<!-- wdef:value or wdef:novalue with no temporary @wdef:value and no qualifier -->
+	<xsl:template match="wdef:value[substring(@wdef:id, 1, 1) != '?' and not(wdef:qualifier)]" />
+	<xsl:template match="wdef:novalue[substring(@wdef:id, 1, 1) != '?' and not(wdef:qualifier)]" />
+	
+	<!-- wdef:qualifier with no wdef:value having temporary @wdef:id -->
+	<xsl:template match="wdef:qualifier[wdef:property/wdef:value[substring(@wdef:id, 1, 1) != '?']]" />
+</xsl:stylesheet>
diff --git a/xslts/get_next_step.xslt b/xslts/get_next_step.xslt
new file mode 100644
index 0000000..a7977ff
--- /dev/null
+++ b/xslts/get_next_step.xslt
@@ -0,0 +1,244 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE wdef:knowledge[
+	<!ENTITY AP "&apos;">
+]>
+
+<xsl:stylesheet version="1.0" exclude-result-prefixes=""
+                xmlns:wdef="https://purl.choffet.net/wdef"
+                xmlns:xml="http://www.w3.org/XML/1998/namespace"
+                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+<!-- get_next_step.xslt - Output import next step one-liner.
+     Copyright (C) 2020-2021  Pierre Choffet
+
+     This program is free software: you can redistribute it and/or modify
+     it under the terms of version 3 of the GNU General Public License as
+     published by the Free Software Foundation.
+
+     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 General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+     -->
+	<xsl:output method="text" encoding="utf-8" indent="yes" />
+	
+	<xsl:template match="text()" />
+	
+	<xsl:template match="/wdef:knowledge/wdef:element[substring(@wdef:id, 1, 1) = '?']">
+		<xsl:text>create-entity </xsl:text>
+		<xsl:value-of select="@wdef:id" />
+		<xsl:text> {</xsl:text>
+		<xsl:if test="wdef:label">
+			<xsl:text>"labels":{</xsl:text>
+				<xsl:apply-templates select="wdef:label" />
+			<xsl:text>}</xsl:text>
+		</xsl:if>
+		<xsl:if test="wdef:label and wdef:description">
+			<xsl:text>, </xsl:text>
+		</xsl:if>
+		<xsl:if test="wdef:description">
+			<xsl:text>"descriptions":{</xsl:text>
+				<xsl:apply-templates select="wdef:description" />
+			<xsl:text>}</xsl:text>
+		</xsl:if>
+		<xsl:text>}&#xa;</xsl:text>
+	</xsl:template>
+	
+	<xsl:template match="wdef:label">
+		<xsl:variable name="text">
+			<xsl:call-template name="escape-quotes">
+				<xsl:with-param name="text" select="." />
+			</xsl:call-template>
+		</xsl:variable>
+		
+		<xsl:text>"</xsl:text>
+		<xsl:value-of select="@wdef:interface-lang" />
+		<xsl:text>": "</xsl:text>
+		<xsl:value-of select="$text" />
+		<xsl:text>"</xsl:text>
+		<xsl:if test="position() != last()">
+			<xsl:text>, </xsl:text>
+		</xsl:if>
+	</xsl:template>
+	
+	<xsl:template match="wdef:description">
+		<xsl:variable name="text">
+			<xsl:call-template name="escape-quotes">
+				<xsl:with-param name="text" select="." />
+			</xsl:call-template>
+		</xsl:variable>
+		<xsl:text>"</xsl:text>
+		<xsl:value-of select="@wdef:interface-lang" />
+		<xsl:text>": "</xsl:text>
+		<xsl:value-of select="$text" />
+		<xsl:text>"</xsl:text>
+		<xsl:if test="position() != last()">
+			<xsl:text>, </xsl:text>
+		</xsl:if>
+	</xsl:template>
+	
+	<xsl:template match="/wdef:knowledge/wdef:element[substring(@wdef:id, 1, 1) != '?']/wdef:property/wdef:value[substring(@wdef:id, 1, 1) = '?' and * and not(.//ref-element[substring(., 1, 1) = '?'])]">
+		<xsl:text>add-claim </xsl:text>
+		<xsl:value-of select="@wdef:id" />
+		<xsl:text> </xsl:text>
+		<xsl:value-of select="../../@wdef:id" />
+		<xsl:text> </xsl:text>
+		<xsl:value-of select="../@wdef:pid" />
+		<xsl:text> </xsl:text>
+		
+		<xsl:choose>
+			<xsl:when test="wdef:literal">
+				<xsl:value-of select="wdef:literal" />
+			</xsl:when>
+			<xsl:when test="wdef:quantity">
+				<xsl:text>{"amount": "</xsl:text>
+				<xsl:value-of select="wdef:quantity" />
+				<xsl:text>", "unit": "</xsl:text>
+				<xsl:value-of select="wdef:quantity/@wdef:unit" />
+				<xsl:text>"}</xsl:text>
+			</xsl:when>
+			<xsl:when test="wdef:qualifier"></xsl:when>
+			<xsl:when test="wdef:ref-element">
+				<xsl:value-of select="wdef:ref-element" />
+			</xsl:when>
+			<xsl:when test="wdef:time">
+				<xsl:text>{"time": "</xsl:text>
+				<xsl:choose>
+					<xsl:when test="wdef:time/@wdef:precision = '11'">
+						<xsl:value-of select="substring(wdef:time, 2, 10)" />
+					</xsl:when>
+					<xsl:when test="wdef:time/@wdef:precision = '10'">
+						<xsl:value-of select="substring(wdef:time, 2, 7)" />
+					</xsl:when>
+					<xsl:when test="wdef:time/@wdef:precision = '9'">
+						<xsl:value-of select="substring(wdef:time, 2, 4)" />
+					</xsl:when>
+					<xsl:otherwise>
+						<xsl:message terminate="yes">
+							<xsl:text>Cannot import time with precision"</xsl:text>
+							<xsl:value-of select="wdef:time/@wdef:precision" />
+							<xsl:text>" for now. Exiting.</xsl:text>
+						</xsl:message>
+					</xsl:otherwise>
+				</xsl:choose>
+				<xsl:text>", "precision": </xsl:text>
+				<xsl:value-of select="wdef:time/@wdef:precision" />
+				<xsl:text>, "calendar": "</xsl:text>
+				<xsl:value-of select="wdef:time/@wdef:calendar" />
+				<xsl:text>"}</xsl:text>
+			</xsl:when>
+			<xsl:when test="wdef:translation">
+				<xsl:text>{"language": "</xsl:text>
+				<xsl:value-of select="wdef:translation/@xml:lang" />
+				<xsl:text>", "text": "</xsl:text>
+				<xsl:value-of select="wdef:translation" />
+				<xsl:text>"}</xsl:text>
+			</xsl:when>
+			<xsl:when test="wdef:coordinate">
+				<xsl:text>{"latitude": </xsl:text>
+				<xsl:value-of select="wdef:coordinate/@wdef:latitude" />
+				<xsl:text>, "longitude": </xsl:text>
+				<xsl:value-of select="wdef:coordinate/@wdef:longitude" />
+				<xsl:text>, "precision": </xsl:text>
+				<xsl:value-of select="wdef:coordinate/@wdef:precision" />
+				<xsl:text>, "globe": "http://www.wikidata.org/entity/</xsl:text>
+				<xsl:value-of select="wdef:coordinate/wdef:ref-element" />
+				<xsl:text>"}</xsl:text>
+			</xsl:when>
+			<xsl:otherwise>
+				<xsl:message terminate="yes">
+					<xsl:text>Cannot import "</xsl:text>
+					<xsl:value-of select="name(*[1])" />
+					<xsl:text>" type for now. Exiting.</xsl:text>
+				</xsl:message>
+			</xsl:otherwise>
+		</xsl:choose>
+		
+		<xsl:text>&#xa;</xsl:text>
+	</xsl:template>
+	
+	<xsl:template match="/wdef:knowledge/wdef:element[substring(@wdef:id, 1, 1) != '?']/wdef:property/wdef:novalue[substring(@wdef:id, 1, 1) = '?']">
+		<xsl:text>add-claim-no-value </xsl:text>
+		<xsl:value-of select="@wdef:id" />
+		<xsl:text> </xsl:text>
+		<xsl:value-of select="../../@wdef:id" />
+		<xsl:text> </xsl:text>
+		<xsl:value-of select="../@wdef:pid" />
+		<xsl:text>&#xa;</xsl:text>
+	</xsl:template>
+	
+	<xsl:template match="/wdef:knowledge/wdef:element[substring(@wdef:id, 1, 1) != '?']/wdef:property/wdef:value[substring(@wdef:id, 1, 1) != '?']/wdef:qualifier/wdef:property/wdef:value | /wdef:knowledge/wdef:element[substring(@wdef:id, 1, 1) != '?']/wdef:property/wdef:novalue[substring(@wdef:id, 1, 1) != '?']/wdef:qualifier/wdef:property/wdef:value">
+		<xsl:text>add-qualifier </xsl:text>
+		<xsl:value-of select="@wdef:id" />
+		<xsl:text> </xsl:text>
+		<xsl:value-of select="../../../@wdef:id" />
+		<xsl:text> </xsl:text>
+		<xsl:value-of select="../@wdef:pid" />
+		<xsl:text> </xsl:text>
+		<xsl:choose>
+			<xsl:when test="wdef:literal">
+				<xsl:value-of select="wdef:literal" />
+			</xsl:when>
+			<xsl:when test="wdef:quantity">
+				<xsl:text>{"amount": "</xsl:text>
+				<xsl:value-of select="wdef:quantity" />
+				<xsl:text>", "unit": "</xsl:text>
+				<xsl:value-of select="wdef:quantity/@wdef:unit" />
+				<xsl:text>"}</xsl:text>
+			</xsl:when>
+			<xsl:when test="wdef:ref-element">
+				<xsl:value-of select="wdef:ref-element" />
+			</xsl:when>
+			<xsl:when test="wdef:time">
+				<xsl:text>{"time": "</xsl:text>
+				<xsl:value-of select="substring(wdef:time, 2)" />
+				<xsl:text>", "precision": "</xsl:text>
+				<xsl:value-of select="wdef:time/@wdef:precision" />
+				<xsl:text>", "calendar": "</xsl:text>
+				<xsl:value-of select="wdef:time/@wdef:calendar" />
+				<xsl:text>"}</xsl:text>
+			</xsl:when>
+			<xsl:otherwise>
+				<xsl:message terminate="yes">
+					<xsl:text>Cannot import "</xsl:text>
+					<xsl:value-of select="name(*[1])" />
+					<xsl:text>" type for now. Exiting.</xsl:text>
+				</xsl:message>
+			</xsl:otherwise>
+		</xsl:choose>
+		<xsl:text>&#xa;</xsl:text>
+	</xsl:template>
+	
+	<xsl:template name="escape-quotes">
+		<xsl:param name="text" />
+		
+		<xsl:call-template name="escape-target">
+			<xsl:with-param name="text" select="$text" />
+			<xsl:with-param name="target" select="'&quot;'" />
+			<xsl:with-param name="escaper" select="'\'" />
+		</xsl:call-template>
+	</xsl:template>
+
+	<xsl:template name="escape-target">
+		<xsl:param name="text" />
+		<xsl:param name="target" />
+		<xsl:param name="escaper" />
+		
+		<xsl:choose>
+			<xsl:when test="contains($text, $target)">
+				<xsl:value-of select="substring-before($text, $target)" />
+				<xsl:value-of select="concat($escaper, $target)" />
+				<xsl:call-template name="escape-target">
+					<xsl:with-param name="text" select="substring-after($text, $target)" />
+					<xsl:with-param name="target" select="$target" />
+					<xsl:with-param name="escaper" select="$escaper" />
+				</xsl:call-template>
+			</xsl:when>
+			<xsl:otherwise>
+				<xsl:value-of select="$text" />
+			</xsl:otherwise>
+		</xsl:choose>
+	</xsl:template>
+</xsl:stylesheet>
diff --git a/xslts/isolate_station.xslt b/xslts/isolate_station.xslt
new file mode 100644
index 0000000..f3d5bc0
--- /dev/null
+++ b/xslts/isolate_station.xslt
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0" xmlns:wdef="https://purl.choffet.net/wdef"
+                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+<!-- isolate_station.xslt - Reduce wdef into a single station.
+     Copyright (C) 2021  Pierre Choffet
+
+     This program is free software: you can redistribute it and/or modify
+     it under the terms of version 3 of the GNU General Public License as
+     published by the Free Software Foundation.
+
+     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 General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+     -->
+	<xsl:output method="xml" encoding="utf-8" />
+
+	<xsl:param name="wigos-id" />
+	
+	<xsl:template match="node()|@*">
+		<xsl:copy>
+			<xsl:apply-templates select="node()|@*" />
+		</xsl:copy>
+	</xsl:template>
+	
+	<xsl:template match="/wdef:knowledge">
+		<xsl:choose>
+			<xsl:when test="wdef:element[wdef:property[@wdef:pid = 'P4136']/wdef:value/wdef:literal = $wigos-id]">
+				<xsl:copy>
+					<xsl:apply-templates select="wdef:element[wdef:property[@wdef:pid = 'P4136']/wdef:value/wdef:literal = $wigos-id]" />
+				</xsl:copy>
+			</xsl:when>
+			<xsl:otherwise>
+				<xsl:message terminate="yes">
+					<xsl:text>No station with </xsl:text>
+					<xsl:value-of select="$wigos-id" />
+					<xsl:text> WIGOS id found.</xsl:text>
+				</xsl:message>
+			</xsl:otherwise>
+		</xsl:choose>
+	</xsl:template>
+</xsl:stylesheet>
diff --git a/xslts/remove_labels_descriptions.xslt b/xslts/remove_labels_descriptions.xslt
new file mode 100644
index 0000000..e873be9
--- /dev/null
+++ b/xslts/remove_labels_descriptions.xslt
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0" xmlns:wdef="https://purl.choffet.net/wdef"
+                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+<!-- remove_labels_descriptions.xslt - Remove labels and descritions from element.
+     Copyright (C) 2020-2021  Pierre Choffet
+
+     This program is free software: you can redistribute it and/or modify
+     it under the terms of version 3 of the GNU General Public License as
+     published by the Free Software Foundation.
+
+     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 General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+     -->
+	<xsl:output method="xml" encoding="utf-8" indent="yes" />
+	<xsl:strip-space elements="*"/>
+	
+	<xsl:param name="wdef-id" />
+	
+	<xsl:template match="node()|@*">
+		<xsl:copy>
+			<xsl:apply-templates select="node()|@*" />
+		</xsl:copy>
+	</xsl:template>
+	
+	<xsl:template match="wdef:label|wdef:description">
+		<xsl:if test="not(../@wdef:id) or ../@wdef:id != $wdef-id">
+			<xsl:copy>
+				<xsl:apply-templates select="node()|@*" />
+			</xsl:copy>
+		</xsl:if>
+	</xsl:template>
+</xsl:stylesheet>
diff --git a/xslts/replace_id.xslt b/xslts/replace_id.xslt
new file mode 100644
index 0000000..a8bf420
--- /dev/null
+++ b/xslts/replace_id.xslt
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0" xmlns:wdef="https://purl.choffet.net/wdef"
+                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+<!-- replace_id.xslt - Replace wdef id.
+     Copyright (C) 2020-2021  Pierre Choffet
+
+     This program is free software: you can redistribute it and/or modify
+     it under the terms of version 3 of the GNU General Public License as
+     published by the Free Software Foundation.
+
+     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 General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+     -->
+	<xsl:output method="xml" encoding="utf-8" />
+	
+	<xsl:param name="old-id" />
+	<xsl:param name="new-id" />
+	
+	<xsl:template match="@*">
+		<xsl:copy>
+			<xsl:apply-templates select="@*" />
+		</xsl:copy>
+	</xsl:template>
+	
+	<xsl:template match="node()">
+		<xsl:copy>
+			<xsl:choose>
+				<xsl:when test="not(@wdef:id) or @wdef:id != $old-id">
+					<xsl:apply-templates select="node()|@*" />
+				</xsl:when>
+				<xsl:otherwise>
+					<xsl:apply-templates select="@*" />
+					<xsl:attribute name="wdef:id">
+						<xsl:value-of select="$new-id" />
+					</xsl:attribute>
+					<xsl:apply-templates select="node()" />
+				</xsl:otherwise>
+			</xsl:choose>
+		</xsl:copy>
+	</xsl:template>
+	
+	<xsl:template match="wdef:ref-element">
+		<xsl:copy>
+			<xsl:choose>
+				<xsl:when test=". = $old-id">
+					<xsl:value-of select="$new-id" />
+				</xsl:when>
+				<xsl:otherwise>
+					<xsl:apply-templates select="node()|@*" />
+				</xsl:otherwise>
+			</xsl:choose>
+		</xsl:copy>
+	</xsl:template>
+</xsl:stylesheet>
-- 
2.48.1