[PATCH] Export dives to UDDF file

Miika Turkia miika.turkia at gmail.com
Sun Mar 24 01:00:25 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 tested this with evaluation version of Diving Log and temperature and
profile data is imported properly. However, pressure and meta data seems
to be missing.  (This information is also missing from DM4 import so
probably not an issue with the Subsurface export.)
---
 divelist.c            |   95 ++++++++++++++++++++++++
 divelist.h            |    1 +
 gtk-gui.c             |    2 +
 xslt/uddf-export.xslt |  191 +++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 289 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..107234e
--- /dev/null
+++ b/xslt/uddf-export.xslt
@@ -0,0 +1,191 @@
+<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 version="3.2.0">
+      <generator>
+        <manufacturer id="subsurface">
+          <name>Subsurface Team</name>
+          <contact>http://subsurface.hohndel.org/</contact>
+        </manufacturer>
+        <version>
+          <xsl:value-of select="/divelog/@version"/>
+        </version>
+        <xsl:if test="/divelog/generator/@date != ''">
+          <datetime>
+            <xsl:value-of select="concat(/divelog/generator/@date, 'T', /divelog/generator/@time)"/>
+          </datetime>
+        </xsl:if>
+      </generator>
+
+      <diver>
+        <owner id="1">
+          <equipment>
+            <xsl:for-each select="/divelog/settings/divecomputerid">
+              <divecomputer id="{./@deviceid}">
+                <name>
+                  <xsl:choose>
+                    <xsl:when test="./@nickname != ''">
+                      <xsl:value-of select="./@nickname"/>
+                    </xsl:when>
+                    <xsl:otherwise>
+                      <xsl:value-of select="./@model"/>
+                    </xsl:otherwise>
+                  </xsl:choose>
+                </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 id="{generate-id(.)}">
+
+      <informationbeforedive>
+        <xsl:if test="node()/temperature/@air != ''">
+          <airtemperature>
+            <xsl:value-of select="format-number(substring-before(node()/temperature/@air, ' ') + 273.15, '0.00')"/>
+          </airtemperature>
+        </xsl:if>
+        <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>
+        <xsl:if test="./cylinder[1]/@size">
+          <tankvolume>
+            <xsl:value-of select="substring-before(./cylinder[1]/@size, ' ')"/>
+          </tankvolume>
+        </xsl:if>
+        <xsl:if test="./cylinder[1]/@start">
+          <tankpressurebegin>
+            <xsl:value-of select="substring-before(./cylinder[1]/@start, ' ')"/>
+          </tankpressurebegin>
+        </xsl:if>
+        <xsl:if test="./cylinder[1]/@end">
+          <tankpressureend>
+            <xsl:value-of select="substring-before(./cylinder[1]/@end, ' ')"/>
+          </tankpressureend>
+        </xsl:if>
+      </tankdata>
+
+      <informationafterdive>
+        <xsl:if test="node()/depth/@max != ''">
+          <greatestdepth>
+            <xsl:value-of select="substring-before(node()/depth/@max, ' ')"/>
+          </greatestdepth>
+        </xsl:if>
+        <xsl:if test="node()/depth/@mean != ''">
+          <averagedepth>
+            <xsl:value-of select="substring-before(node()/depth/@mean, ' ')"/>
+          </averagedepth>
+        </xsl:if>
+        <xsl:if test="./@duration != ''">
+          <diveduration>
+            <xsl:call-template name="time2sec">
+              <xsl:with-param name="time">
+                <xsl:value-of select="./@duration"/>
+              </xsl:with-param>
+            </xsl:call-template>
+          </diveduration>
+        </xsl:if>
+        <xsl:if test="node()/temperature/@water != ''">
+          <lowesttemperature>
+            <xsl:value-of select="format-number(substring-before(node()/temperature/@water, ' ') + 273.15, '0.00')"/>
+          </lowesttemperature>
+        </xsl:if>
+        <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