[PATCH] Export dives to UDDF file

Miika Turkia miika.turkia at gmail.com
Sat Mar 23 09:19:59 PDT 2013


This patch implements exporting dives from Subsurface to UDDF format.
Events and cylinder info are the most remarkable things still missing
from the export.

Signed-off-by: Miika Turkia <miika.turkia at gmail.com>
---
I would appreciate if someone with a divelog that supports UDDF import
would give this a test. I have tried it with Subsurface but obviously
there is quite a bit of data discarded on export-import cycle. This is
hopefully mainly due to import missing features, but could be the export
as well...
---
 divelist.c            |   95 +++++++++++++++++++++++++
 divelist.h            |    1 +
 gtk-gui.c             |    2 +
 xslt/uddf-export.xslt |  186 +++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 284 insertions(+)
 create mode 100644 xslt/uddf-export.xslt

diff --git a/divelist.c b/divelist.c
index c25ee62..198e2d6 100644
--- a/divelist.c
+++ b/divelist.c
@@ -2102,6 +2102,92 @@ static void export_selected_dives_cb(GtkWidget *menuitem, GtkTreePath *path)
 }
 #endif
 
+#if defined(XSLT)
+static void export_dives_uddf(const gboolean selected)
+{
+	FILE *f;
+	char *filename = NULL;
+	size_t streamsize;
+	char *membuf;
+	xmlDoc *doc;
+	xsltStylesheetPtr xslt = NULL;
+	xmlDoc *transformed;
+	GtkWidget *dialog;
+
+	dialog = gtk_file_chooser_dialog_new(_("Export As UDDF File"),
+			GTK_WINDOW(main_window),
+			GTK_FILE_CHOOSER_ACTION_SAVE,
+			GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+			GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+			NULL);
+	gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
+
+	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
+		filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+	}
+	gtk_widget_destroy(dialog);
+
+	if (!filename)
+		return;
+
+	/* Save XML to file and convert it into a memory buffer */
+	save_dives_logic(filename, selected);
+	f = fopen(filename, "r");
+	fseek(f, 0, SEEK_END);
+	streamsize = ftell(f);
+	rewind(f);
+
+	membuf = malloc(streamsize + 1);
+	if (!membuf || !fread(membuf, streamsize, 1, f)) {
+		fprintf(stderr, "Failed to read memory buffer\n");
+		return;
+	}
+	membuf[streamsize] = 0;
+	fclose(f);
+	g_unlink(filename);
+
+	/*
+	 * Parse the memory buffer into XML document and
+	 * transform it to UDDF format, finally dumping
+	 * the XML into a character buffer.
+	 */
+	doc = xmlReadMemory(membuf, strlen(membuf), "divelog", NULL, 0);
+	if (!doc) {
+		fprintf(stderr, "Failed to read XML memory\n");
+		return;
+	}
+	free((void *)membuf);
+
+	/* Convert to UDDF format */
+	xslt = get_stylesheet("uddf-export.xslt");
+	if (!xslt) {
+		fprintf(stderr, "Failed to open UDDF conversion stylesheet\n");
+		return;
+	}
+	transformed = xsltApplyStylesheet(xslt, doc, NULL);
+	xsltFreeStylesheet(xslt);
+	xmlFreeDoc(doc);
+
+	/* Write the transformed XML to file */
+	f = g_fopen(filename, "w");
+	xmlDocFormatDump(f, transformed, 1);
+	xmlFreeDoc(transformed);
+
+	fclose(f);
+	g_free(filename);
+}
+
+static void export_selected_dives_uddf_cb(GtkWidget *menuitem, GtkTreePath *path)
+{
+	export_dives_uddf(TRUE);
+}
+
+void export_all_dives_uddf_cb()
+{
+	export_dives_uddf(FALSE);
+}
+#endif
+
 static void merge_dive_index(int i, struct dive *a)
 {
 	struct dive *b = get_dive(i+1);
@@ -2171,6 +2257,9 @@ static void popup_divelist_menu(GtkTreeView *tree_view, GtkTreeModel *model, int
 	char deleteplurallabel[] = N_("Delete dives");
 	char deletesinglelabel[] = N_("Delete dive");
 	char *deletelabel;
+#if defined(XSLT)
+	char exportuddflabel[] = N_("Export dive(s) to UDDF");
+#endif
 #if defined(LIBZIP) && defined(XSLT)
 	char exportlabel[] = N_("Export dive(s)");
 #endif
@@ -2247,6 +2336,12 @@ static void popup_divelist_menu(GtkTreeView *tree_view, GtkTreeModel *model, int
 			gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
 #endif
 
+#if defined(XSLT)
+			menuitem = gtk_menu_item_new_with_label(exportuddflabel);
+			g_signal_connect(menuitem, "activate", G_CALLBACK(export_selected_dives_uddf_cb), path);
+			gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+#endif
+
 			menuitem = gtk_menu_item_new_with_label(editlabel);
 			g_signal_connect(menuitem, "activate", G_CALLBACK(edit_selected_dives_cb), NULL);
 			gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
diff --git a/divelist.h b/divelist.h
index 47f79e2..856318e 100644
--- a/divelist.h
+++ b/divelist.h
@@ -15,4 +15,5 @@ extern void select_next_dive(void);
 extern void select_prev_dive(void);
 extern void show_and_select_dive(struct dive *dive);
 extern double init_decompression(struct dive * dive);
+extern void export_all_dives_uddf_cb();
 #endif
diff --git a/gtk-gui.c b/gtk-gui.c
index c5b9a80..82e9a31 100644
--- a/gtk-gui.c
+++ b/gtk-gui.c
@@ -1509,6 +1509,7 @@ static GtkActionEntry menu_items[] = {
 	{ "CloseFile",      GTK_STOCK_CLOSE, N_("Close"), NULL, NULL, G_CALLBACK(file_close) },
 	{ "Print",          GTK_STOCK_PRINT, N_("Print..."),  CTRLCHAR "P", NULL, G_CALLBACK(do_print) },
 	{ "ImportFile",     NULL, N_("Import File(s)..."), CTRLCHAR "I", NULL, G_CALLBACK(import_files) },
+	{ "ExportUDDF",     NULL, N_("Export UDDF..."), NULL, NULL, G_CALLBACK(export_all_dives_uddf_cb) },
 	{ "DownloadLog",    NULL, N_("Download From Dive Computer..."), CTRLCHAR "D", NULL, G_CALLBACK(download_dialog) },
 	{ "DownloadWeb",    GTK_STOCK_CONNECT, N_("Download From Web Service..."), NULL, NULL, G_CALLBACK(webservice_download_dialog) },
 	{ "AddDive",        GTK_STOCK_ADD, N_("Add Dive..."), NULL, NULL, G_CALLBACK(add_dive_cb) },
@@ -1550,6 +1551,7 @@ static const gchar* ui_string = " \
 				<menuitem name=\"Close\" action=\"CloseFile\" /> \
 				<separator name=\"Separator1\"/> \
 				<menuitem name=\"Import XML File\" action=\"ImportFile\" /> \
+				<menuitem name=\"Export to UDDF File\" action=\"ExportUDDF\" /> \
 				<separator name=\"Separator2\"/> \
 				<menuitem name=\"Print\" action=\"Print\" /> \
 				<separator name=\"Separator3\"/> \
diff --git a/xslt/uddf-export.xslt b/xslt/uddf-export.xslt
new file mode 100644
index 0000000..1097990
--- /dev/null
+++ b/xslt/uddf-export.xslt
@@ -0,0 +1,186 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+  <xsl:import href="commonTemplates.xsl"/>
+  <xsl:strip-space elements="*"/>
+  <xsl:output method="xml" encoding="utf-8" indent="yes"/>
+
+  <xsl:template match="/divelog/dives">
+    <uddf xmlns="http://www.streit.cc/uddf/3.2/" version="3.2.0">
+      <generator>
+        <manufacturer id="subsurface">
+          <name>Subsurface Team</name>
+          <contact>http://subsurface.hohndel.org/</contact>
+        </manufacturer>
+        <version>
+          <xsl:value-of select="subsurfaceversion"/>
+        </version>
+        <datetime>
+          <xsl:if test="/divelog/generator/@date != ''">
+            <xsl:value-of select="concat(/divelog/generator/@date, 'T', /divelog/generator/@time)"/>
+          </xsl:if>
+        </datetime>
+      </generator>
+
+      <diver>
+        <xsl:variable name="owner">
+          <xsl:choose>
+            <xsl:when test="/divelog/owner != ''">
+              <xsl:value-of select="generate-id(/divelog/owner)"/>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="'owner'"/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:variable>
+        <owner id="{$owner}">
+          <personal>
+            <firstname>
+              <xsl:value-of select="/divelog/owner/@firstname"/>
+            </firstname>
+            <lastname>
+              <xsl:value-of select="/divelog/owner/@lastname"/>
+            </lastname>
+          </personal>
+          <equipment>
+            <xsl:for-each select="/divelog/settings/divecomputerid">
+              <divecomputer id="{./@deviceid}">
+                <name>
+                  <xsl:value-of select="./@model"/>
+                </name>
+                <model>
+                  <xsl:value-of select="./@model"/>
+                </model>
+              </divecomputer>
+            </xsl:for-each>
+          </equipment>
+        </owner>
+      </diver>
+
+      <!-- Gas definitions is not yet working, so it is commented out
+           -->
+      <xsl:if test="'asdf' = ''">
+        <gasdefinitions>
+          <xsl:for-each select="dive/cylinder">
+            <mix id="{generate-id(.)}">
+              <name>
+                <xsl:value-of select="concat(./@o2, '/', ./he)"/>
+              </name>
+              <o2>
+                <xsl:value-of select="./@o2"/>
+              </o2>
+              <he>
+                <xsl:value-of select="./@he"/>
+              </he>
+            </mix>
+          </xsl:for-each>
+        </gasdefinitions>
+      </xsl:if>
+
+
+      <profiledata>
+        <xsl:for-each select="trip">
+          <repetitiongroup id="{generate-id(.)}">
+            <xsl:apply-templates select="dive"/>
+          </repetitiongroup>
+        </xsl:for-each>
+        <xsl:for-each select="dive">
+          <repetitiongroup id="{generate-id(.)}">
+            <xsl:apply-templates select="."/>
+          </repetitiongroup>
+        </xsl:for-each>
+      </profiledata>
+    </uddf>
+  </xsl:template>
+
+  <xsl:template match="dive">
+    <dive xmlns="http://www.streit.cc/uddf/3.2/" id="{generate-id(.)}">
+
+      <informationbeforedive>
+        <airtemperature>
+          <xsl:value-of select="format-number(substring-before(node()/temperature/@air, ' ') + 273.15, '0.00')"/>
+        </airtemperature>
+        <datetime>
+          <xsl:value-of select="concat(./@date, 'T', ./@time)"/>
+        </datetime>
+        <divenumber>
+          <xsl:value-of select="./@number"/>
+        </divenumber>
+      </informationbeforedive>
+
+      <samples>
+        <xsl:for-each select="./divecomputer[1]/sample">
+          <waypoint>
+            <depth>
+              <xsl:value-of select="substring-before(./@depth, ' ')"/>
+            </depth>
+            <divetime>
+              <xsl:call-template name="time2sec">
+                <xsl:with-param name="time">
+                  <xsl:value-of select="./@time"/>
+                </xsl:with-param>
+              </xsl:call-template>
+            </divetime>
+            <xsl:if test="./@pressure != ''">
+              <tankpressure>
+                <xsl:value-of select="substring-before(./@pressure, ' ')"/>
+              </tankpressure>
+            </xsl:if>
+            <xsl:if test="./@temp != ''">
+              <temperature>
+                <xsl:value-of select="format-number(substring-before(./@temp, ' ') + 273.15, '0.00')"/>
+              </temperature>
+            </xsl:if>
+          </waypoint>
+        </xsl:for-each>
+      </samples>
+
+      <tankdata>
+        <tankvolume>
+          <xsl:value-of select="substring-before(./cylinder[1]/@size, ' ')"/>
+        </tankvolume>
+        <tankpressurebegin>
+          <xsl:value-of select="substring-before(./cylinder[1]/@start, ' ')"/>
+        </tankpressurebegin>
+        <tankpressureend>
+          <xsl:value-of select="substring-before(./cylinder[1]/@end, ' ')"/>
+        </tankpressureend>
+      </tankdata>
+
+      <informationafterdive>
+        <greatestdepth>
+          <xsl:value-of select="substring-before(node()/depth/@max, ' ')"/>
+        </greatestdepth>
+        <averagedepth>
+          <xsl:value-of select="substring-before(node()/depth/@mean, ' ')"/>
+        </averagedepth>
+        <diveduration>
+          <xsl:call-template name="time2sec">
+            <xsl:with-param name="time">
+              <xsl:value-of select="./@duration"/>
+            </xsl:with-param>
+          </xsl:call-template>
+        </diveduration>
+        <lowesttemperature>
+          <xsl:value-of select="format-number(substring-before(node()/temperature/@water, ' ') + 273.15, '0.00')"/>
+        </lowesttemperature>
+        <notes>
+          <para>
+            <xsl:value-of select="notes"/>
+          </para>
+        </notes>
+        <rating>
+          <ratingvalue>
+            <xsl:choose>
+              <xsl:when test="./@rating = 0">
+                <xsl:value-of select="'1'"/>
+              </xsl:when>
+              <xsl:when test="./@rating != ''">
+                <xsl:value-of select="./@rating * 2"/>
+              </xsl:when>
+            </xsl:choose>
+          </ratingvalue>
+        </rating>
+      </informationafterdive>
+
+    </dive>
+  </xsl:template>
+</xsl:stylesheet>
-- 
1.7.9.5



More information about the subsurface mailing list