Blame src/gom-facebook-miner.c

Packit f01ec2
/*
Packit f01ec2
 * GNOME Online Miners - crawls through your online content
Packit f01ec2
 * Copyright (c) 2013 Álvaro Peña
Packit f01ec2
 *
Packit f01ec2
 * This program is free software; you can redistribute it and/or
Packit f01ec2
 * modify it under the terms of the GNU General Public License
Packit f01ec2
 * as published by the Free Software Foundation; either version 2
Packit f01ec2
 * of the License, or (at your option) any later version.
Packit f01ec2
 *
Packit f01ec2
 * This program is distributed in the hope that it will be useful,
Packit f01ec2
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit f01ec2
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit f01ec2
 * GNU General Public License for more details.
Packit f01ec2
 *
Packit f01ec2
 * You should have received a copy of the GNU General Public License
Packit f01ec2
 * along with this program; if not, write to the Free Software
Packit f01ec2
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit f01ec2
 * 02110-1301, USA.
Packit f01ec2
 *
Packit f01ec2
 * Author: Álvaro Peña <alvaropg@gmail.com>
Packit f01ec2
 *
Packit f01ec2
 */
Packit f01ec2
Packit f01ec2
#include "config.h"
Packit f01ec2
Packit f01ec2
#include <goa/goa.h>
Packit f01ec2
#include <gfbgraph/gfbgraph.h>
Packit f01ec2
#include <gfbgraph/gfbgraph-goa-authorizer.h>
Packit f01ec2
Packit f01ec2
#include "gom-facebook-miner.h"
Packit f01ec2
Packit f01ec2
#define MINER_IDENTIFIER "gd:facebook:miner:9972c7ff-a30f-4dd4-bc77-1adf9dd14364"
Packit f01ec2
Packit f01ec2
G_DEFINE_TYPE (GomFacebookMiner, gom_facebook_miner, GOM_TYPE_MINER)
Packit f01ec2
Packit f01ec2
static gboolean
Packit f01ec2
account_miner_job_process_photo (GomAccountMinerJob *job,
Packit f01ec2
                                 TrackerSparqlConnection *connection,
Packit f01ec2
                                 GHashTable *previous_resources,
Packit f01ec2
                                 const gchar *datasource_urn,
Packit f01ec2
                                 GFBGraphPhoto *photo,
Packit f01ec2
                                 const gchar *parent_resource_urn,
Packit f01ec2
                                 const gchar *creator,
Packit f01ec2
                                 GCancellable *cancellable,
Packit f01ec2
                                 GError **error)
Packit f01ec2
{
Packit f01ec2
  GTimeVal new_mtime;
Packit f01ec2
  const gchar *photo_id;
Packit f01ec2
  const gchar *photo_name;
Packit f01ec2
  const gchar *photo_created_time;
Packit f01ec2
  const gchar *photo_updated_time;
Packit f01ec2
  const gchar *photo_link;
Packit f01ec2
  gchar *identifier;
Packit f01ec2
  const gchar *class = "nmm:Photo";
Packit f01ec2
  gchar *resource = NULL;
Packit f01ec2
  gboolean resource_exists, mtime_changed;
Packit f01ec2
  gchar *contact_resource;
Packit f01ec2
Packit f01ec2
  photo_id = gfbgraph_node_get_id (GFBGRAPH_NODE (photo));
Packit f01ec2
  photo_link = gfbgraph_node_get_link (GFBGRAPH_NODE (photo));
Packit f01ec2
  photo_created_time = gfbgraph_node_get_created_time (GFBGRAPH_NODE (photo));
Packit f01ec2
  photo_name = gfbgraph_photo_get_name (photo);
Packit f01ec2
Packit f01ec2
  identifier = g_strdup_printf ("facebook:%s", photo_id);
Packit f01ec2
Packit f01ec2
  /* remove from the list of the previous resources */
Packit f01ec2
  g_hash_table_remove (previous_resources, identifier);
Packit f01ec2
Packit f01ec2
  resource = gom_tracker_sparql_connection_ensure_resource
Packit f01ec2
    (connection,
Packit f01ec2
     cancellable, error,
Packit f01ec2
     &resource_exists,
Packit f01ec2
     datasource_urn, identifier,
Packit f01ec2
     "nfo:RemoteDataObject", class, NULL);
Packit f01ec2
Packit f01ec2
  if (*error != NULL)
Packit f01ec2
    goto out;
Packit f01ec2
Packit f01ec2
  gom_tracker_update_datasource (connection, datasource_urn,
Packit f01ec2
                                 resource_exists, identifier, resource,
Packit f01ec2
                                 cancellable, error);
Packit f01ec2
  if (*error != NULL)
Packit f01ec2
    goto out;
Packit f01ec2
Packit f01ec2
  photo_updated_time = gfbgraph_node_get_updated_time (GFBGRAPH_NODE (photo));
Packit f01ec2
  if (!g_time_val_from_iso8601 (photo_updated_time, &new_mtime))
Packit f01ec2
    g_warning ("Can't convert updated time from ISO 8601 (%s) to a GTimeVal struct",
Packit f01ec2
               photo_updated_time);
Packit f01ec2
  else
Packit f01ec2
    {
Packit f01ec2
      mtime_changed = gom_tracker_update_mtime (connection, new_mtime.tv_sec,
Packit f01ec2
                                                resource_exists, identifier, resource,
Packit f01ec2
                                                cancellable, error);
Packit f01ec2
      if (*error != NULL)
Packit f01ec2
        goto out;
Packit f01ec2
Packit f01ec2
      /* avoid updating the DB if the entry already exists and has not
Packit f01ec2
       * been modified since our last run.
Packit f01ec2
       */
Packit f01ec2
      if (!mtime_changed)
Packit f01ec2
        goto out;
Packit f01ec2
  }
Packit f01ec2
Packit f01ec2
  /* the resource changed - just set all the properties again */
Packit f01ec2
  gom_tracker_sparql_connection_insert_or_replace_triple
Packit f01ec2
    (connection,
Packit f01ec2
     cancellable, error,
Packit f01ec2
     datasource_urn, resource,
Packit f01ec2
     "nie:url", photo_link);
Packit f01ec2
Packit f01ec2
  if (*error != NULL)
Packit f01ec2
    goto out;
Packit f01ec2
Packit f01ec2
  gom_tracker_sparql_connection_insert_or_replace_triple
Packit f01ec2
    (connection,
Packit f01ec2
     cancellable, error,
Packit f01ec2
     datasource_urn, resource,
Packit f01ec2
     "nie:isPartOf", parent_resource_urn);
Packit f01ec2
Packit f01ec2
  if (*error != NULL)
Packit f01ec2
    goto out;
Packit f01ec2
Packit f01ec2
  gom_tracker_sparql_connection_insert_or_replace_triple
Packit f01ec2
    (connection,
Packit f01ec2
     cancellable, error,
Packit f01ec2
     datasource_urn, resource,
Packit f01ec2
     "nie:mimeType", "image/jpeg");
Packit f01ec2
Packit f01ec2
  if (*error != NULL)
Packit f01ec2
    goto out;
Packit f01ec2
Packit f01ec2
  gom_tracker_sparql_connection_insert_or_replace_triple
Packit f01ec2
    (connection,
Packit f01ec2
     cancellable, error,
Packit f01ec2
     datasource_urn, resource,
Packit f01ec2
     "nie:title", photo_name);
Packit f01ec2
Packit f01ec2
  if (*error != NULL)
Packit f01ec2
    goto out;
Packit f01ec2
Packit f01ec2
  contact_resource = gom_tracker_utils_ensure_contact_resource
Packit f01ec2
    (connection,
Packit f01ec2
     cancellable, error,
Packit f01ec2
     datasource_urn, creator);
Packit f01ec2
Packit f01ec2
  if (*error != NULL)
Packit f01ec2
    goto out;
Packit f01ec2
Packit f01ec2
  gom_tracker_sparql_connection_insert_or_replace_triple
Packit f01ec2
    (connection,
Packit f01ec2
     cancellable, error,
Packit f01ec2
     datasource_urn, resource,
Packit f01ec2
     "nco:creator", contact_resource);
Packit f01ec2
Packit f01ec2
  g_free (contact_resource);
Packit f01ec2
  if (*error != NULL)
Packit f01ec2
    goto out;
Packit f01ec2
Packit f01ec2
  gom_tracker_sparql_connection_insert_or_replace_triple
Packit f01ec2
    (connection,
Packit f01ec2
     cancellable, error,
Packit f01ec2
     datasource_urn, resource,
Packit f01ec2
     "nie:contentCreated", photo_created_time);
Packit f01ec2
Packit f01ec2
  if (*error != NULL)
Packit f01ec2
    goto out;
Packit f01ec2
Packit f01ec2
 out:
Packit f01ec2
  g_free (resource);
Packit f01ec2
  g_free (identifier);
Packit f01ec2
Packit f01ec2
  if (*error != NULL)
Packit f01ec2
    return FALSE;
Packit f01ec2
Packit f01ec2
  return TRUE;
Packit f01ec2
}
Packit f01ec2
Packit f01ec2
/* TODO: Until GFBGraph parse the "from" node section, we require the
Packit f01ec2
 *  album creator (generally the logged user)
Packit f01ec2
 */
Packit f01ec2
static gboolean
Packit f01ec2
account_miner_job_process_album (GomAccountMinerJob *job,
Packit f01ec2
                                 TrackerSparqlConnection *connection,
Packit f01ec2
                                 GHashTable *previous_resources,
Packit f01ec2
                                 const gchar *datasource_urn,
Packit f01ec2
                                 GFBGraphAlbum *album,
Packit f01ec2
                                 const gchar *creator,
Packit f01ec2
                                 GCancellable *cancellable,
Packit f01ec2
                                 GError **error)
Packit f01ec2
{
Packit f01ec2
  const gchar *album_id;
Packit f01ec2
  const gchar *album_name;
Packit f01ec2
  const gchar *album_description;
Packit f01ec2
  const gchar *album_link;
Packit f01ec2
  const gchar *album_created_time;
Packit f01ec2
  gchar *identifier;
Packit f01ec2
  const gchar *class = "nfo:DataContainer";
Packit f01ec2
  gchar *resource = NULL;
Packit f01ec2
  gboolean resource_exists;
Packit f01ec2
  gchar *contact_resource;
Packit f01ec2
  GList *l;
Packit f01ec2
  GList *photos = NULL;
Packit f01ec2
  GFBGraphAuthorizer *authorizer;
Packit f01ec2
Packit f01ec2
  authorizer = GFBGRAPH_AUTHORIZER (g_hash_table_lookup (job->services, "photos"));
Packit f01ec2
  album_id = gfbgraph_node_get_id (GFBGRAPH_NODE (album));
Packit f01ec2
  album_link = gfbgraph_node_get_link (GFBGRAPH_NODE (album));
Packit f01ec2
  album_created_time = gfbgraph_node_get_created_time (GFBGRAPH_NODE (album));
Packit f01ec2
  album_name = gfbgraph_album_get_name (album);
Packit f01ec2
  album_description = gfbgraph_album_get_description (album);
Packit f01ec2
Packit f01ec2
  identifier = g_strdup_printf ("photos:collection:facebook:%s", album_id);
Packit f01ec2
Packit f01ec2
  /* remove from the list of the previous resources */
Packit f01ec2
  g_hash_table_remove (previous_resources, identifier);
Packit f01ec2
Packit f01ec2
  resource = gom_tracker_sparql_connection_ensure_resource
Packit f01ec2
    (connection,
Packit f01ec2
     cancellable, error,
Packit f01ec2
     &resource_exists,
Packit f01ec2
     datasource_urn, identifier,
Packit f01ec2
     "nfo:RemoteDataObject", class,
Packit f01ec2
     NULL);
Packit f01ec2
Packit f01ec2
  if (*error != NULL)
Packit f01ec2
    goto out;
Packit f01ec2
Packit f01ec2
  gom_tracker_update_datasource (connection, datasource_urn,
Packit f01ec2
                                 resource_exists, identifier, resource,
Packit f01ec2
                                 cancellable, error);
Packit f01ec2
Packit f01ec2
  if (*error != NULL)
Packit f01ec2
    goto out;
Packit f01ec2
Packit f01ec2
  /* TODO: Check updated time to avoid updating the album if has not
Packit f01ec2
   * been modified since our last run
Packit f01ec2
   */
Packit f01ec2
Packit f01ec2
  gom_tracker_sparql_connection_insert_or_replace_triple
Packit f01ec2
    (connection,
Packit f01ec2
     cancellable, error,
Packit f01ec2
     datasource_urn, resource,
Packit f01ec2
     "nie:url", album_link);
Packit f01ec2
Packit f01ec2
  if (*error != NULL)
Packit f01ec2
    goto out;
Packit f01ec2
Packit f01ec2
  gom_tracker_sparql_connection_insert_or_replace_triple
Packit f01ec2
    (connection,
Packit f01ec2
     cancellable, error,
Packit f01ec2
     datasource_urn, resource,
Packit f01ec2
     "nie:description", album_description);
Packit f01ec2
Packit f01ec2
  if (*error != NULL)
Packit f01ec2
    goto out;
Packit f01ec2
Packit f01ec2
  gom_tracker_sparql_connection_insert_or_replace_triple
Packit f01ec2
    (connection,
Packit f01ec2
     cancellable, error,
Packit f01ec2
     datasource_urn, resource,
Packit f01ec2
     "nie:title", album_name);
Packit f01ec2
Packit f01ec2
  if (*error != NULL)
Packit f01ec2
    goto out;
Packit f01ec2
Packit f01ec2
  contact_resource = gom_tracker_utils_ensure_contact_resource
Packit f01ec2
    (connection,
Packit f01ec2
     cancellable, error,
Packit f01ec2
     datasource_urn, creator);
Packit f01ec2
Packit f01ec2
  if (*error != NULL)
Packit f01ec2
    goto out;
Packit f01ec2
Packit f01ec2
  gom_tracker_sparql_connection_insert_or_replace_triple
Packit f01ec2
    (connection,
Packit f01ec2
     cancellable, error,
Packit f01ec2
     datasource_urn, resource,
Packit f01ec2
     "nco:creator", contact_resource);
Packit f01ec2
  g_free (contact_resource);
Packit f01ec2
Packit f01ec2
  if (*error != NULL)
Packit f01ec2
    goto out;
Packit f01ec2
Packit f01ec2
  gom_tracker_sparql_connection_insert_or_replace_triple
Packit f01ec2
    (connection,
Packit f01ec2
     cancellable, error,
Packit f01ec2
     datasource_urn, resource,
Packit f01ec2
     "nie:contentCreated", album_created_time);
Packit f01ec2
Packit f01ec2
  if (*error != NULL)
Packit f01ec2
    goto out;
Packit f01ec2
Packit f01ec2
  /* Album photos */
Packit f01ec2
  photos = gfbgraph_node_get_connection_nodes (GFBGRAPH_NODE (album),
Packit f01ec2
                                               GFBGRAPH_TYPE_PHOTO,
Packit f01ec2
                                               authorizer,
Packit f01ec2
                                               error);
Packit f01ec2
  if (*error != NULL)
Packit f01ec2
    goto out;
Packit f01ec2
Packit f01ec2
  for (l = photos; l != NULL; l = l->next)
Packit f01ec2
    {
Packit f01ec2
      GError *local_error = NULL;
Packit f01ec2
      GFBGraphPhoto *photo = GFBGRAPH_PHOTO (l->data);
Packit f01ec2
Packit f01ec2
      account_miner_job_process_photo (job,
Packit f01ec2
                                       connection,
Packit f01ec2
                                       previous_resources,
Packit f01ec2
                                       datasource_urn,
Packit f01ec2
                                       photo,
Packit f01ec2
                                       resource,
Packit f01ec2
                                       creator,
Packit f01ec2
                                       cancellable,
Packit f01ec2
                                       &local_error);
Packit f01ec2
      if (local_error != NULL)
Packit f01ec2
        {
Packit f01ec2
          const gchar *photo_id;
Packit f01ec2
Packit f01ec2
          photo_id = gfbgraph_node_get_id (GFBGRAPH_NODE (photo));
Packit f01ec2
          g_warning ("Unable to process %s: %s", photo_id, local_error->message);
Packit f01ec2
          g_clear_error (&local_error);
Packit f01ec2
        }
Packit f01ec2
    }
Packit f01ec2
Packit f01ec2
 out:
Packit f01ec2
  g_free (resource);
Packit f01ec2
  g_free (identifier);
Packit f01ec2
Packit f01ec2
  g_list_free_full (photos, g_object_unref);
Packit f01ec2
Packit f01ec2
  if (*error != NULL)
Packit f01ec2
    return FALSE;
Packit f01ec2
Packit f01ec2
  return TRUE;
Packit f01ec2
}
Packit f01ec2
Packit f01ec2
static void
Packit f01ec2
query_facebook (GomAccountMinerJob *job,
Packit f01ec2
                TrackerSparqlConnection *connection,
Packit f01ec2
                GHashTable *previous_resources,
Packit f01ec2
                const gchar *datasource_urn,
Packit f01ec2
                GCancellable *cancellable,
Packit f01ec2
                GError **error)
Packit f01ec2
{
Packit f01ec2
  GFBGraphAuthorizer *authorizer;
Packit f01ec2
  GFBGraphUser *me = NULL;
Packit f01ec2
  const gchar *me_name;
Packit f01ec2
  GList *albums = NULL;
Packit f01ec2
  GList *l = NULL;
Packit f01ec2
  GError *local_error = NULL;
Packit f01ec2
Packit f01ec2
  authorizer = GFBGRAPH_AUTHORIZER (g_hash_table_lookup (job->services, "photos"));
Packit f01ec2
  if (authorizer == NULL)
Packit f01ec2
    {
Packit f01ec2
      /* FIXME: use proper #defines and enumerated types */
Packit f01ec2
      g_set_error (&local_error,
Packit f01ec2
                   g_quark_from_static_string ("gom-error"),
Packit f01ec2
                   0,
Packit f01ec2
                   "Can not query without a service");
Packit f01ec2
      goto out;
Packit f01ec2
    }
Packit f01ec2
Packit f01ec2
  me = gfbgraph_user_get_me (authorizer, &local_error);
Packit f01ec2
  if (local_error != NULL)
Packit f01ec2
    goto out;
Packit f01ec2
Packit f01ec2
  me_name = gfbgraph_user_get_name (me);
Packit f01ec2
Packit f01ec2
  albums = gfbgraph_user_get_albums (me, authorizer, &local_error);
Packit f01ec2
  if (local_error != NULL)
Packit f01ec2
    goto out;
Packit f01ec2
Packit f01ec2
  for (l = albums; l != NULL; l = l->next)
Packit f01ec2
    {
Packit f01ec2
      GFBGraphAlbum *album = GFBGRAPH_ALBUM (l->data);
Packit f01ec2
Packit f01ec2
      account_miner_job_process_album (job,
Packit f01ec2
                                       connection,
Packit f01ec2
                                       previous_resources,
Packit f01ec2
                                       datasource_urn,
Packit f01ec2
                                       album,
Packit f01ec2
                                       me_name,
Packit f01ec2
                                       cancellable,
Packit f01ec2
                                       &local_error);
Packit f01ec2
      if (local_error != NULL)
Packit f01ec2
        {
Packit f01ec2
          const gchar *album_id;
Packit f01ec2
Packit f01ec2
          album_id = gfbgraph_node_get_id (GFBGRAPH_NODE (album));
Packit f01ec2
          g_warning ("Unable to process %s: %s", album_id, local_error->message);
Packit f01ec2
          g_clear_error (&local_error);
Packit f01ec2
        }
Packit f01ec2
    }
Packit f01ec2
Packit f01ec2
 out:
Packit f01ec2
  if (local_error != NULL)
Packit f01ec2
    g_propagate_error (error, local_error);
Packit f01ec2
Packit f01ec2
  g_list_free_full (albums, g_object_unref);
Packit f01ec2
  g_clear_object (&me);
Packit f01ec2
}
Packit f01ec2
Packit f01ec2
static GHashTable *
Packit f01ec2
create_services (GomMiner *self,
Packit f01ec2
                 GoaObject *object)
Packit f01ec2
{
Packit f01ec2
  GFBGraphGoaAuthorizer *authorizer;
Packit f01ec2
  GError *error = NULL;
Packit f01ec2
  GHashTable *services;
Packit f01ec2
Packit f01ec2
  services = g_hash_table_new_full (g_str_hash, g_str_equal,
Packit f01ec2
                                    NULL, (GDestroyNotify) g_object_unref);
Packit f01ec2
Packit f01ec2
  authorizer = gfbgraph_goa_authorizer_new (object);
Packit f01ec2
Packit f01ec2
  if (gom_miner_supports_type (self, "photos"))
Packit f01ec2
    {
Packit f01ec2
      gfbgraph_authorizer_refresh_authorization (GFBGRAPH_AUTHORIZER (authorizer), NULL, &error);
Packit f01ec2
      if (error != NULL)
Packit f01ec2
        {
Packit f01ec2
          g_warning ("Error refreshing authorization (%d): %s", error->code, error->message);
Packit f01ec2
          g_error_free (error);
Packit f01ec2
        }
Packit f01ec2
Packit f01ec2
      g_hash_table_insert (services, "photos", authorizer);
Packit f01ec2
    }
Packit f01ec2
Packit f01ec2
  return services;
Packit f01ec2
}
Packit f01ec2
Packit f01ec2
static void
Packit f01ec2
gom_facebook_miner_init (GomFacebookMiner *miner)
Packit f01ec2
{
Packit f01ec2
}
Packit f01ec2
Packit f01ec2
static void
Packit f01ec2
gom_facebook_miner_class_init (GomFacebookMinerClass *klass)
Packit f01ec2
{
Packit f01ec2
  GomMinerClass *miner_class = GOM_MINER_CLASS (klass);
Packit f01ec2
Packit f01ec2
  miner_class->goa_provider_type = "facebook";
Packit f01ec2
  miner_class->miner_identifier = MINER_IDENTIFIER;
Packit f01ec2
  miner_class->version = 2;
Packit f01ec2
Packit f01ec2
  miner_class->create_services = create_services;
Packit f01ec2
  miner_class->query = query_facebook;
Packit f01ec2
}