[tracker/miner-gdata] Add GData miner



commit b2e3668ad497cc5b4e32361b66331efabbe45418
Author: Adrien Bustany <abustany gnome org>
Date:   Fri Mar 5 20:53:59 2010 -0300

    Add GData miner
    
    So far the miner only exploits the PicasaWeb service

 configure.ac                                       |   34 ++
 data/dbus/Makefile.am                              |    5 +
 ...freedesktop.Tracker1.Miner.PicasaWeb.service.in |    3 +
 data/miners/Makefile.am                            |    6 +
 data/miners/tracker-miner-picasaweb.desktop.in     |    7 +
 src/miners/Makefile.am                             |    4 +
 src/miners/gdata/Makefile.am                       |   67 +++
 src/miners/gdata/tracker-config.vala               |  101 ++++
 src/miners/gdata/tracker-miner-gdata.vala          |   87 ++++
 src/miners/gdata/tracker-miner-picasaweb.vala      |  511 ++++++++++++++++++++
 10 files changed, 825 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index ec10dd5..38b88dc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -211,6 +211,7 @@ GNOME_KEYRING_REQUIRED=2.26
 LIBGRSS_REQUIRED=0.3
 REST_REQUIRED=0.6
 NETWORK_MANAGER_REQUIRED=0.8
+LIBGDATA_REQUIRED=0.6
 
 # 3.6.11 for sqlite_backup API
 # 3.6.16 to fix test failures
@@ -411,6 +412,14 @@ PKG_CHECK_MODULES(TRACKER_MINER_FLICKR, [$TRACKER_MINER_FLICKR_REQUIRED],
 
 TRACKER_MINER_FLICKR_LIBS="$TRACKER_MINER_FLICKR_LIBS -lz -lm"
 
+# Check requirements for tracker-miner-gdata
+TRACKER_MINER_GDATA_REQUIRED="glib-2.0     >= $GLIB_REQUIRED
+                              libgdata     >= $LIBGDATA_REQUIRED"
+
+PKG_CHECK_MODULES(TRACKER_MINER_GDATA, [$TRACKER_MINER_GDATA_REQUIRED],
+                  [have_tracker_miner_gdata=yes],
+                  [have_tracker_miner_gdata=no])
+
 # Check requirements for tracker-utils
 TRACKER_UTILS_REQUIRED="glib-2.0     >= $GLIB_REQUIRED
                         gio-unix-2.0 >= $GLIB_REQUIRED
@@ -1112,6 +1121,29 @@ fi
 AM_CONDITIONAL(HAVE_TRACKER_MINER_FLICKR, test "x$have_tracker_miner_flickr" = "xyes")
 
 ##################################################################
+# Check for tracker-miner-gdata
+##################################################################
+
+AC_ARG_ENABLE(miner_gdata,
+              AS_HELP_STRING([--enable-miner-gdata],
+                             [enable GData miner [[default=auto]]]),,
+                             [enable_tracker_miner_gdata=auto])
+
+enable_tracker_miner_gdata=$enable_miner_gdata
+
+if test "x$enable_tracker_miner_gdata" = "xyes"; then
+   if test "x$have_tracker_miner_gdata" != "xyes"; then
+      AC_MSG_ERROR([Couldn't find tracker-miner-gdata dependencies ($TRACKER_MINER_GDATA_REQUIRED).])
+   fi
+else
+   if test "x$enable_tracker_miner_gdata" = "xno"; then
+      have_tracker_miner_gdata="no  (disabled)"
+   fi
+fi
+
+AM_CONDITIONAL(HAVE_TRACKER_MINER_GDATA, test "x$have_tracker_miner_gdata" = "xyes")
+
+##################################################################
 # Check for tracker-miner-rss
 ##################################################################
 
@@ -2057,6 +2089,7 @@ AC_CONFIG_FILES([
 	src/Makefile
 	src/miners/Makefile
 	src/miners/fs/Makefile
+	src/miners/gdata/Makefile
 	src/miners/rss/Makefile
 	src/miners/flickr/Makefile
 	src/tracker-store/Makefile
@@ -2209,6 +2242,7 @@ Data Miners:
 	RSS:                                    $have_tracker_miner_rss
 	Evolution:                              $have_tracker_miner_evolution ($tracker_miner_evolution_install_dir)
 	Flickr:                                 $have_tracker_miner_flickr
+	GData:                                  $have_tracker_miner_gdata
 
 Plugins:
 
diff --git a/data/dbus/Makefile.am b/data/dbus/Makefile.am
index 4549048..e6cb85f 100644
--- a/data/dbus/Makefile.am
+++ b/data/dbus/Makefile.am
@@ -23,6 +23,7 @@ service_in_files =                                     \
 	org.freedesktop.Tracker1.Miner.Files.service.in \
 	org.freedesktop.Tracker1.Miner.Flickr.service.in \
 	org.freedesktop.Tracker1.Miner.RSS.service.in  \
+	org.freedesktop.Tracker1.Miner.PicasaWeb.service.in	\
 	org.freedesktop.Tracker1.Extract.service.in
 
 %.service: %.service.in
@@ -48,6 +49,10 @@ if HAVE_TRACKER_MINER_FLICKR
 service_DATA += org.freedesktop.Tracker1.Miner.Flickr.service
 endif
 
+if HAVE_TRACKER_MINER_GDATA
+service_DATA += org.freedesktop.Tracker1.Miner.PicasaWeb.service
+endif
+
 
 EXTRA_DIST =                                           \
 	$(service_in_files)                            \
diff --git a/data/dbus/org.freedesktop.Tracker1.Miner.PicasaWeb.service.in b/data/dbus/org.freedesktop.Tracker1.Miner.PicasaWeb.service.in
new file mode 100644
index 0000000..520ecdc
--- /dev/null
+++ b/data/dbus/org.freedesktop.Tracker1.Miner.PicasaWeb.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.freedesktop.Tracker1.Miner.PicasaWeb
+Exec= libexecdir@/tracker-miner-gdata
diff --git a/data/miners/Makefile.am b/data/miners/Makefile.am
index be36aa4..a1c08ff 100644
--- a/data/miners/Makefile.am
+++ b/data/miners/Makefile.am
@@ -5,6 +5,7 @@ desktop_in_in_files =                                  \
 	tracker-miner-evolution.desktop.in.in          \
 	tracker-miner-files.desktop.in.in              \
 	tracker-miner-flickr.desktop.in.in             \
+	tracker-miner-picasaweb.desktop.in             \
 	tracker-miner-rss.desktop.in.in
 
 desktop_in_files =                                     \
@@ -12,6 +13,7 @@ desktop_in_files =                                     \
 	tracker-miner-evolution.desktop.in             \
 	tracker-miner-files.desktop.in                 \
 	tracker-miner-flickr.desktop.in                \
+	tracker-miner-picasaweb.desktop                \
 	tracker-miner-rss.desktop.in
 
 tracker_minersdir = $(datadir)/tracker/miners
@@ -32,6 +34,10 @@ if HAVE_TRACKER_MINER_FLICKR
 tracker_miners_DATA += tracker-miner-flickr.desktop
 endif
 
+if HAVE_TRACKER_MINER_GDATA
+tracker_miners_DATA += tracker-miner-picasaweb.desktop
+endif
+
 @INTLTOOL_DESKTOP_RULE@
 
 %.desktop.in: %.desktop.in.in
diff --git a/data/miners/tracker-miner-picasaweb.desktop.in b/data/miners/tracker-miner-picasaweb.desktop.in
new file mode 100644
index 0000000..c115942
--- /dev/null
+++ b/data/miners/tracker-miner-picasaweb.desktop.in
@@ -0,0 +1,7 @@
+[Desktop Entry]
+Encoding=UTF-8
+_Name=PicasaWeb
+_Comment=PicasaWeb miner
+DBusName=org.freedesktop.Tracker1.Miner.PicasaWeb
+DBusPath=/org/freedesktop/Tracker1/Miner/PicasaWeb
+AuthenticationMethod=UserPass
diff --git a/src/miners/Makefile.am b/src/miners/Makefile.am
index 0cf41ea..20bc33a 100644
--- a/src/miners/Makefile.am
+++ b/src/miners/Makefile.am
@@ -9,3 +9,7 @@ endif
 if HAVE_TRACKER_MINER_FLICKR
 SUBDIRS += flickr
 endif
+
+if HAVE_TRACKER_MINER_GDATA
+SUBDIRS += gdata
+endif
diff --git a/src/miners/gdata/Makefile.am b/src/miners/gdata/Makefile.am
new file mode 100644
index 0000000..01a9185
--- /dev/null
+++ b/src/miners/gdata/Makefile.am
@@ -0,0 +1,67 @@
+include $(top_srcdir)/Makefile.decl
+
+INCLUDES =								\
+	-Wall								\
+	-DSHAREDIR=\""$(datadir)"\"					\
+	-DPKGLIBDIR=\""$(libdir)/tracker"\"				\
+	-DLOCALEDIR=\""$(localedir)"\" 					\
+	-DLIBEXEC_PATH=\""$(libexecdir)"\"				\
+	-DG_LOG_DOMAIN=\"Tracker\"					\
+	-DTRACKER_COMPILATION						\
+	-I$(top_srcdir)/src						\
+	-I$(top_srcdir)/src/libtracker-sparql				\
+	$(WARN_CFLAGS)							\
+	$(GMODULE_CFLAGS)						\
+	$(PANGO_CFLAGS)							\
+	$(DBUS_CFLAGS)							\
+	$(TRACKER_MINER_GDATA_CFLAGS)						\
+	$(GCOV_CFLAGS)
+
+VALAFLAGS =								\
+	--pkg gio-2.0							\
+	--pkg posix							\
+	--pkg libgdata							\
+	--thread
+
+libexec_PROGRAMS = tracker-miner-gdata
+
+tracker_miner_gdata_VALASOURCES =					\
+	$(top_srcdir)/src/libtracker-common/libtracker-common.vapi	\
+	tracker-config.vala						\
+	tracker-miner-gdata.vala					\
+	tracker-miner-picasaweb.vala					\
+	$(top_srcdir)/src/libtracker-sparql/tracker-sparql- TRACKER_API_VERSION@.vapi
+
+tracker_miner_gdata_SOURCES =						\
+	$(tracker_miner_gdata_VALASOURCES:.vala=.c)
+
+tracker_miner_gdata_LDADD =						\
+	$(top_builddir)/src/libtracker-miner/libtracker-miner- TRACKER_API_VERSION@.la	\
+	$(top_builddir)/src/libtracker-sparql/libtracker-sparql.la	\
+	$(DBUS_LIBS)							\
+	$(GMODULE_LIBS)							\
+	$(GTHREAD_LIBS)							\
+	$(GIO_LIBS)							\
+	$(GCOV_LIBS)							\
+	$(GLIB2_LIBS)							\
+	$(TRACKER_MINER_GDATA_LIBS)							\
+	-lz								\
+	-lm
+
+vapi_sources =								\
+	$(top_srcdir)/src/libtracker-miner/tracker-miner- TRACKER_API_VERSION@.vapi
+
+tracker-miner-gdata.vala.stamp: $(tracker_miner_gdata_VALASOURCES) $(vapi_sources)
+	$(AM_V_GEN)$(VALAC) $(GCOV_VALAFLAGS) -C $(VALAFLAGS) $^
+	touch $@
+
+
+BUILT_SOURCES = tracker-miner-gdata.vala.stamp
+
+MAINTAINERCLEANFILES =							\
+	tracker-miner-gdata.vala.stamp				\
+	$(tracker_miner_gdata_VALASOURCES:.vala=.c)
+
+EXTRA_DIST =								\
+	$(tracker_miner_gdata_VALASOURCES)				\
+	tracker-miner-gdata.vala.stamp
diff --git a/src/miners/gdata/tracker-config.vala b/src/miners/gdata/tracker-config.vala
new file mode 100644
index 0000000..7806ca4
--- /dev/null
+++ b/src/miners/gdata/tracker-config.vala
@@ -0,0 +1,101 @@
+public class Tracker.Config : Tracker.ConfigFile {
+	public enum DownloadBehaviour {
+		GPRS, /* Download if connected via GPRS, EDGE, 3G or LAN */
+		EDGE, /* Download if connected via EDGE, 3G or LAN */
+		@3G,  /* Download if connected via 3G or LAN */
+		LAN   /* Download only if connected to a LAN */
+	}
+
+	/* GLib.KeyFile defines */
+	public static const string GROUP_GENERAL = "General";
+	public static const string GROUP_NETWORK = "Network";
+
+	private struct ConfigKey {
+		GLib.Type type;
+		string property;
+		string group;
+		string key;
+
+		ConfigKey (GLib.Type type, string property, string group, string key) {
+			this.type = type;
+			this.property = property;
+			this.group = group;
+			this.key = key;
+		}
+	}
+
+	static List<ConfigKey?> config_keys = new List<ConfigKey?> ();
+
+	/* Default values */
+	public static const uint DEFAULT_DOWNLOAD_BEHAVIOUR = (uint)DownloadBehaviour  3G;
+
+	private uint _download_behaviour = DEFAULT_DOWNLOAD_BEHAVIOUR;
+	public uint download_behaviour {
+		get {
+			return _download_behaviour;
+		}
+		set {
+			if (value < (uint)DownloadBehaviour.LAN || value > (uint)DownloadBehaviour.GPRS) {
+				return;
+			}
+			_download_behaviour = value;
+		}
+	}
+
+	static construct {
+		config_keys.append(ConfigKey (typeof (int), "download-behaviour", GROUP_NETWORK, "DownloadBehaviour"));
+	}
+
+	construct {
+		domain = "tracker-miner-gdata";
+		base.constructed ();
+
+		load (true);
+	}
+
+	public override void changed () {
+		load (false);
+	}
+
+	public void load (bool use_defaults) {
+		if (use_defaults) {
+			create_with_defaults ();
+		}
+
+		if (!file_exists) {
+			base.save ();
+		}
+
+		foreach (unowned ConfigKey key in config_keys) {
+			if (key.type == typeof (int)) {
+				KeyfileObject.load_int (this, key.property, key_file, key.group, key.key);
+			} else {
+				assert_not_reached ();
+			}
+		}
+	}
+
+	public new bool save () {
+		if (key_file == null) {
+			critical ("Could not save config, GKeyFile was NULL, has the config been loaded?");
+			return false;
+		}
+
+		KeyfileObject.save_int (this, "download-behaviour", key_file, GROUP_NETWORK, "DownloadBehaviour");
+
+		return base.save ();
+	}
+
+	private void create_with_defaults () {
+		try {
+			key_file.has_key (GROUP_NETWORK, "DownloadBehaviour");
+		} catch (Error e) {
+			if (!(e is KeyFileError.KEY_NOT_FOUND) && !(e is KeyFileError.GROUP_NOT_FOUND)) {
+				critical ("Could not load config default: %s", e.message);
+			} else {
+				key_file.set_integer (GROUP_NETWORK, "DownloadBehaviour",
+				                      DownloadBehaviour  3G);
+			}
+		}
+	}
+}
diff --git a/src/miners/gdata/tracker-miner-gdata.vala b/src/miners/gdata/tracker-miner-gdata.vala
new file mode 100644
index 0000000..dfd42a5
--- /dev/null
+++ b/src/miners/gdata/tracker-miner-gdata.vala
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2010, Adrien Bustany <abustany gnome org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+namespace Tracker {
+
+public class MinerGData {
+	private static MinerPicasaweb miner_picasaweb;
+	private static const string NMM_PHOTO = "http://www.tracker-project.org/temp/nmm#Photo";;
+
+	private static MainLoop main_loop;
+
+	private static bool in_loop = false;
+	private static void signal_handler (int signo) {
+		if (in_loop) {
+			Posix.exit (Posix.EXIT_FAILURE);
+		}
+
+		switch (signo) {
+			case Posix.SIGINT:
+			case Posix.SIGTERM:
+				in_loop = true;
+				main_loop.quit ();
+				break;
+		}
+	}
+
+	private static void init_signals () {
+#if G_OS_WIN32
+#else
+		Posix.sigaction_t act = Posix.sigaction_t ();
+		Posix.sigset_t    empty_mask = Posix.sigset_t ();
+		Posix.sigemptyset (empty_mask);
+		act.sa_handler = signal_handler;
+		act.sa_mask    = empty_mask;
+		act.sa_flags   = 0;
+
+		Posix.sigaction (Posix.SIGTERM, act, null);
+		Posix.sigaction (Posix.SIGINT, act, null);
+#endif
+	}
+
+//	private static void writeback_cb (GLib.HashTable<string, void*> resources) {
+//		List<weak string> uris = (List<weak string>)resources.get_keys ();
+//		weak string[] rdf_classes;
+//
+//		foreach (string uri in uris) {
+//			rdf_classes = (string[])resources.lookup (uri);
+//
+//			for (uint i = 0 ; rdf_classes[i] != null ; i++) {
+//				if (rdf_classes[i] == NMM_PHOTO) {
+//					//miner_picasaweb.writeback (uri);
+//					return;
+//				}
+//			}
+//		}
+//	}
+
+	public static void main (string[] args) {
+		Environment.set_application_name ("GData tracker miner");
+		miner_picasaweb = new MinerPicasaweb ();
+
+		init_signals ();
+
+		main_loop = new MainLoop (null, false);
+		main_loop.run ();
+
+		miner_picasaweb.shutdown ();
+	}
+}
+
+} // End namespace Tracker
diff --git a/src/miners/gdata/tracker-miner-picasaweb.vala b/src/miners/gdata/tracker-miner-picasaweb.vala
new file mode 100644
index 0000000..928399a
--- /dev/null
+++ b/src/miners/gdata/tracker-miner-picasaweb.vala
@@ -0,0 +1,511 @@
+/*
+ * Copyright (C) 2010, Adrien Bustany <abustany gnome org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+namespace Tracker {
+
+public class MinerPicasaweb : Tracker.MinerWeb {
+	private static const string API_KEY = "ABQIAAAAISU_TtyUrVg0N9RGhb8pTxRI3KVolA1NVmS0lLG3RQFyztcosBSHs5rHiQHsmL41EbX1SehKnf1rrw";
+	private static const string DATASOURCE_URN = "urn:nepomuk:datasource:682654ec-b59d-4e99-9b0f-7559fc9c5d42";
+	private static const string MINER_NAME = "PicasaWeb";
+	private static const string SERVICE_DESCRIPTION = "Tracker miner for PicasaWeb";
+	private static const uint   PULL_INTERVAL = 5*60; /* seconds */
+	private uint pull_timeout_handle;
+
+	private GData.PicasaWebService service = null;
+
+	private Config config;
+	private Sparql.Connection tracker;
+
+	construct {
+		name = MINER_NAME;
+		associated = false;
+		status = "Not authenticated";
+		progress = 0.0;
+	}
+
+	public MinerPicasaweb () {
+		config = new Config ();
+
+		service = new GData.PicasaWebService (API_KEY);
+
+		try {
+			tracker = Sparql.Connection.get ();
+		} catch (Error e) {
+			critical ("Couldn't connect to Tracker: %s", e.message);
+		}
+
+		this.notify["associated"].connect (association_status_changed);
+
+		authenticate ();
+	}
+
+	private void association_status_changed (Object source, ParamSpec pspec) {
+		if (associated) {
+			if (pull_timeout_handle != 0)
+				return;
+
+			message ("Miner is now associated. Initiating periodic pull.");
+			pull_timeout_handle = Timeout.add_seconds (PULL_INTERVAL, pull_timeout_cb);
+			Idle.add ( () => { pull_timeout_cb (); return false; });
+		} else {
+			if (pull_timeout_handle == 0)
+				return;
+
+			Source.remove (pull_timeout_handle);
+		}
+	}
+
+	private bool pull_timeout_cb () {
+		NetworkProviderStatus network_status = NetworkProvider.get ().get_status ();
+		if (network_status == NetworkProviderStatus.DISCONNECTED) {
+			return true;
+		}
+
+		if (network_status == NetworkProviderStatus.GPRS &&
+		    (uint)config.download_behaviour > (uint)Config.DownloadBehaviour.GPRS) {
+			return true;
+		}
+
+		if (network_status == NetworkProviderStatus.EDGE &&
+		    (uint)config.download_behaviour > (uint)Config.DownloadBehaviour.EDGE) {
+			return true;
+		}
+
+		if (network_status == NetworkProviderStatus  3G &&
+		    (uint)config.download_behaviour > (uint)Config DownloadBehaviour  3G) {
+			return true;
+		}
+
+		status = "Refreshing data";
+		progress = 0.0;
+
+		service.query_all_albums_async (null, service.get_username (), null, albums_cb, pull_finished_cb);
+		return true;
+	}
+
+	private void albums_cb (GData.Entry entry, uint entry_key, uint entry_count) {
+		GData.Feed feed_files;
+		GData.PicasaWebAlbum album = entry as GData.PicasaWebAlbum;
+		GData.PicasaWebFile current_file;
+		string current_file_url;
+		string current_file_urn;
+		string current_file_identifier;
+		string current_album_urn;
+		string current_album_identifier;
+		bool resource_created;
+		Sparql.Builder builder;
+		List<string> album_files_urls;
+		TimeVal tv = TimeVal ();
+
+		try {
+			feed_files = service.query_files (album, null, null, null);
+		} catch (Error service_error) {
+			warning ("Couldn't get files for album %s: %s", album.title, service_error.message);
+			return;
+		}
+
+		set ("status", "Refreshing album \"%s\"".printf (album.get_title ()));
+
+		album_files_urls = new List<string> ();
+
+		foreach (GData.Entry current_entry in feed_files.get_entries ()) {
+			current_file = current_entry as GData.PicasaWebFile;
+			current_file_url = ((GData.MediaContent)current_file.get_contents ().first ().data).get_uri ();
+			// The cast to GData.Entry is necessary to get the right id due to
+			// a bug in current libgdata (gdata <= 0.6.1)
+			current_file_identifier = ((GData.Entry)current_file).get_id ();
+			try {
+				current_file_urn = get_resource (current_file_url,
+				                                 {"nmm:Photo", "nfo:RemoteDataObject", "nfo:MediaFileListEntry"},
+				                                 current_file_identifier,
+				                                 out resource_created);
+			} catch (Error e) {
+				warning ("Couldn't get resource for picture %s: %s", current_file_url, e.message);
+				continue;
+			}
+
+			builder = new Sparql.Builder.update ();
+
+			if (resource_created) {
+				builder.insert_open (current_file_url);
+				builder.subject_iri (current_file_urn);
+				builder.predicate ("nie:dataSource");
+				builder.object_iri (DATASOURCE_URN);
+				builder.predicate ("nie:url");
+				builder.object_string (current_file_url);
+			}
+
+			update_triple_string (builder,
+			                      current_file_url,
+			                      current_file_urn,
+			                      "nie:title",
+			                      (current_file.get_caption () != null ? current_file.get_caption () : current_file.get_title ()));
+
+			current_file.get_timestamp (tv);
+			update_triple_string (builder,
+			                      current_file_url,
+			                      current_file_urn,
+			                      "nie:contentCreated",
+			                      tv.to_iso8601 ());
+
+			current_file.get_edited (tv);
+			update_triple_string (builder,
+			                      current_file_url,
+			                      current_file_urn,
+			                      "nie:contentLastModified",
+			                      tv.to_iso8601 ());
+
+			update_triple_int64 (builder,
+			                     current_file_url,
+			                     current_file_urn,
+			                     "nfo:width",
+			                     (int64)current_file.get_width ());
+
+			update_triple_int64 (builder,
+			                     current_file_url,
+			                     current_file_urn,
+			                     "nfo:height",
+			                     (int64)current_file.get_height ());
+
+			if (current_file.get_tags () != null) {
+				builder.insert_open (current_file_url);
+				builder.subject_iri (current_file_urn);
+
+				foreach (string tag_label in current_file.get_tags ().split (",")) {
+					builder.predicate ("nao:hasTag");
+					builder.object_blank_open ();
+					builder.predicate ("a");
+					builder.object ("nao:Tag");
+					builder.predicate ("nao:prefLabel");
+					builder.object_string (tag_label);
+					builder.object_blank_close ();
+				}
+
+				builder.insert_close ();
+			}
+
+			try {
+				tracker.update (builder.result);
+				album_files_urls.prepend (current_file_url);
+			} catch (Error e) {
+				warning ("Couldn't import picture %s: %s", current_file_url, e.message);
+			}
+		}
+
+		builder = new Sparql.Builder.update ();
+
+		current_album_identifier = ((GData.Entry)album).get_id ();
+		try {
+			current_album_urn = get_resource (DATASOURCE_URN,
+			                                  {"nmm:ImageList", "nfo:RemoteDataObject"},
+			                                  current_album_identifier,
+			                                  out resource_created);
+		} catch (Error e) {
+			warning ("Coudln't get resource for album %s: %s", album.get_title (), e.message);
+			return;
+		}
+
+		if (resource_created) {
+			builder.predicate ("nie:dataSource");
+			builder.object_iri (DATASOURCE_URN);
+		}
+
+		update_triple_string (builder,
+		                      DATASOURCE_URN,
+		                      current_album_urn,
+		                      "nie:title",
+		                      album.get_title ());
+
+		if (album.get_summary () != null) {
+			update_triple_string (builder,
+			                      DATASOURCE_URN,
+			                      current_album_urn,
+			                      "rdfs:comment",
+			                      album.get_summary ());
+		}
+
+		if (album.get_tags () != null) {
+			foreach (string tag_label in album.get_tags ().split (",")) {
+				builder.predicate ("nao:hasTag");
+				builder.object_blank_open ();
+				builder.predicate ("nao:prefLabel");
+				builder.object_string (tag_label);
+				builder.object_blank_close ();
+			}
+		}
+
+		update_triple_int64 (builder,
+		                     DATASOURCE_URN,
+		                     current_album_urn,
+		                     "nfo:entryCounter",
+		                     (int64)album.get_num_photos ());
+
+		album.get_timestamp (tv);
+		update_triple_string (builder,
+		                      DATASOURCE_URN,
+		                      current_album_urn,
+		                      "nie:contentCreated",
+		                      tv.to_iso8601 ());
+
+		album.get_edited (tv);
+		update_triple_string (builder,
+		                      DATASOURCE_URN,
+		                      current_album_urn,
+		                      "nie:contentLastModified",
+		                      tv.to_iso8601 ());
+
+		if (album_files_urls.length () > 0) {
+			builder.insert_open (current_album_urn);
+			builder.subject_iri (current_album_urn);
+
+			foreach (string current_url in album_files_urls) {
+				builder.predicate ("nfo:hasMediaFileListEntry");
+				builder.object_blank_open ();
+				builder.predicate ("a");
+				builder.object ("nmm:Photo");
+				builder.predicate ("a");
+				builder.object ("nfo:MediaFileListEntry");
+				builder.predicate ("a");
+				builder.object ("nfo:RemoteDataObject");
+				builder.predicate ("nie:url");
+				builder.object_string (current_url);
+				builder.object_blank_close ();
+			}
+
+			builder.insert_close ();
+		}
+
+		try {
+			tracker.update (builder.result);
+		} catch (Error e) {
+			warning ("Couldn't save album %s: %s", album.title, e.message);
+		}
+
+		progress = (1.0 + entry_key)/entry_count;
+	}
+
+	private void pull_finished_cb () {
+		status = Idle;
+		progress = 1.0;
+	}
+
+	private string get_resource (string? graph, string[] types, string identifier, out bool created) throws GLib.Error {
+		string inner_query;
+		string select_query;
+		string insert_query;
+		Variant query_results;
+		HashTable<string, string> anonymous_nodes;
+
+
+		select_query = "";
+		inner_query = "";
+		created = false;
+
+		foreach (string type in types) {
+			inner_query += " a %s; ".printf (type);
+		}
+		inner_query += "nao:identifier \"%s\"".printf (identifier);
+
+		select_query = "select ?urn where { ?urn %s }".printf (inner_query);
+
+		try {
+			Sparql.Cursor cursor = tracker.query (select_query);
+
+			if (cursor.next ()) {
+				return cursor.get_string (0);
+			}
+		} catch (Error tracker_error) {
+			throw tracker_error;
+		}
+
+		if (graph == null) {
+			insert_query = "insert { _:res %s }".printf (inner_query);
+		} else {
+			insert_query = "insert into <%s> { _:res %s }".printf (graph, inner_query);
+		}
+
+		try {
+			created = true;
+			query_results = tracker.update_blank (insert_query);
+			anonymous_nodes = (HashTable<string, string>) query_results.get_child_value (0).get_child_value(0);
+			return anonymous_nodes.lookup ("res");
+		} catch (Error tracker_error) {
+			throw tracker_error;
+		}
+	}
+
+	public void update_triple_string (Sparql.Builder builder, string graph, string urn, string property, string new_value) {
+		builder.delete_open (graph);
+		builder.subject_iri (urn);
+		builder.predicate (property);
+		builder.object_string (new_value);
+		builder.delete_close ();
+
+		builder.insert_open (graph);
+		builder.subject_iri (urn);
+		builder.predicate (property);
+		builder.object_string (new_value);
+		builder.insert_close ();
+	}
+
+	public void update_triple_int64 (Sparql.Builder builder, string graph, string urn, string property, int64 new_value) {
+		builder.delete_open (graph);
+		builder.subject_iri (urn);
+		builder.predicate (property);
+		builder.object_int64 (new_value);
+		builder.delete_close ();
+
+		builder.insert_open (graph);
+		builder.subject_iri (urn);
+		builder.predicate (property);
+		builder.object_int64 (new_value);
+		builder.insert_close ();
+	}
+
+	public void shutdown () {
+		set ("associated", false);
+	}
+
+	public override void authenticate () throws MinerWebError {
+		PasswordProvider password_provider;
+		string username;
+		string password;
+		bool verified = false;
+
+		password_provider = PasswordProvider.get ();
+
+		set ("associated", false);
+
+		try {
+			password = password_provider.get_password (MINER_NAME, out username);
+		} catch (Error e) {
+			if (e is PasswordProviderError.NOTFOUND) {
+				throw new MinerWebError.NO_CREDENTIALS ("Miner is not associated");
+			}
+			throw new MinerWebError.KEYRING (e.message);
+		}
+
+		try {
+			verified = service.authenticate (username, password, null);
+		} catch (Error service_error) {
+			throw new MinerWebError.SERVICE (service_error.message);
+		}
+
+		if (!verified) {
+			throw new MinerWebError.WRONG_CREDENTIALS ("Wrong username and/or password");
+		}
+
+		message ("Authentication successful");
+		set ("associated", true);
+	}
+
+	public override void dissociate () throws MinerWebError {
+		var password_provider = PasswordProvider.get ();
+
+		try {
+			password_provider.forget_password (MINER_NAME);
+		} catch (Error e) {
+			if (e is PasswordProviderError.SERVICE) {
+				throw new MinerWebError.KEYRING (e.message);
+			}
+
+			critical ("Internal error: %s", e.message);
+			return;
+		}
+
+		set ("associated", false);
+	}
+
+	public override void associate (HashTable<string, string> association_data) throws Tracker.MinerWebError {
+		assert (association_data.lookup ("username") != null && association_data.lookup ("password") != null);
+
+		var password_provider = PasswordProvider.get ();
+
+		try {
+			password_provider.store_password (MINER_NAME,
+			                                  SERVICE_DESCRIPTION,
+			                                  association_data.lookup ("username"),
+			                                  association_data.lookup ("password"));
+		} catch (Error e) {
+			if (e is PasswordProviderError.SERVICE) {
+				throw new MinerWebError.KEYRING (e.message);
+			}
+
+			critical ("Internal error: %s", e.message);
+			return;
+		}
+	}
+
+	public override GLib.HashTable get_association_data () throws Tracker.MinerWebError {
+		return new HashTable<string, string>(str_hash, str_equal);
+	}
+
+//	public async void writeback (string uri) {
+//		weak PtrArray results;
+//		weak string[][] triples;
+//		GData.Query query;
+//		GData.PicasaWebFile file;
+//		string[] local_tags = new string[] {};
+//
+//		try {
+//			results = yield execute_sparql (("select ?photo_id ?tag where {"
+//			                               + "<%s> nie:dataSource <%s> ;"
+//			                               +      "nao:identifier ?photo_id ;"
+//			                               +      "nao:hasTag ?t ."
+//			                               + " ?t  nao:prefLabel ?tag }").printf (uri, DATASOURCE_URN));
+//		} catch (Error tracker_error) {
+//			warning ("Tracker error while doing writeback for photo %s: %s", uri, tracker_error.message);
+//			return;
+//		}
+//
+//		message ("writeback for %s", uri);
+//
+//		if (results.len == 0)
+//			return;
+//
+//		triples = (string[][])results.pdata;
+//
+//		for (uint i = 0 ; i < results.len ; ++i) {
+//			// See http://code.google.com/apis/picasaweb/docs/2.0/developers_guide_protocol.html#Tags
+//			local_tags += triples[i][1].replace (",", "%2C");
+//		}
+//
+//		try {
+//			file = service.query_single_entry (triples[0][0], null, typeof (GData.PicasaWebFile), null) as GData.PicasaWebFile;
+//		} catch (Error gdata_error) {
+//			warning ("GData error while doing writeback for photo %s: %s", uri, gdata_error.message);
+//			return;
+//		}
+//
+//		if (file == null) {
+//			warning ("Could not writeback photo %s: file not found on remote service", uri);
+//			return;
+//		}
+//
+//		file.tags = local_tags;
+//
+//		try {
+//			service.update_entry (file, null);
+//		} catch (Error update_error) {
+//			warning ("Could not writeback photo %s: %s", uri, update_error.message);
+//		}
+//	}
+}
+
+} // End namespace Tracker



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]