[gnome-calendar/wip/flb/weather-forecast: 110/135] weather: Re-use old weather predictions on errors



commit d1ee082142608488338afe25f082b68c9d1fc79d
Author: Florian Brosch <flo brosch gmail com>
Date:   Sun Oct 15 21:58:57 2017 +0200

    weather: Re-use old weather predictions on errors

 src/gcal-application.c     |   6 +-
 src/gcal-weather-service.c | 154 +++++++++++++++++++++++++++++++++++++++------
 src/gcal-weather-service.h |   5 +-
 3 files changed, 143 insertions(+), 22 deletions(-)
---
diff --git a/src/gcal-application.c b/src/gcal-application.c
index efc452eb..a598627e 100644
--- a/src/gcal-application.c
+++ b/src/gcal-application.c
@@ -35,7 +35,8 @@
 #include <gio/gio.h>
 #include <glib/gi18n.h>
 
-#define WEATHER_CHECK_INTERVAL 7200 /* seconds */
+#define WEATHER_CHECK_INTERVAL (2 * 60 * 60)  /* seconds */
+#define WEATHER_VALID_TIMESPAN (24 * 60 * 60) /* seconds */
 #define FORECAST_MAX_DAYS      3
 
 struct _GcalApplication
@@ -424,7 +425,8 @@ gcal_application_init (GcalApplication *self)
   g_signal_connect_swapped (self->manager, "source-changed", G_CALLBACK (process_sources), self);
   self->weather_service = gcal_weather_service_new (NULL, /* in prep. for configurable time zones */
                                                     FORECAST_MAX_DAYS,
-                                                    WEATHER_CHECK_INTERVAL);
+                                                    WEATHER_CHECK_INTERVAL,
+                                                    WEATHER_VALID_TIMESPAN);
   self->search_provider = gcal_shell_search_provider_new ();
   gcal_shell_search_provider_connect (self->search_provider, self->manager);
 }
diff --git a/src/gcal-weather-service.c b/src/gcal-weather-service.c
index 8c7cea62..63932193 100644
--- a/src/gcal-weather-service.c
+++ b/src/gcal-weather-service.c
@@ -51,6 +51,8 @@ typedef struct
  * @location_cancellable:    Used to deal with async location service construction.
  * @locaton_running:         Whether location service is active.
  * @weather_infos:           List of #GcalWeatherInfo objects.
+ * @weather_infos_upated:    The monotonic time @weather_info was set at.
+ * @valid_timespan:          Amount of seconds weather information are considered valid.
  * @gweather_info:           The weather info to query.
  * @max_days:                Number of days we want weather information for.
  * @weather_service_running: True if weather service is active.
@@ -84,6 +86,8 @@ struct _GcalWeatherService
 
   /* weather: */
   GSList          *weather_infos;        /* owned[owned] */
+  gint64           weather_infos_upated;
+  gint64           valid_timespan;
   GWeatherInfo    *gweather_info;        /* owned, nullable */
   guint            max_days;
   gboolean         weather_service_running;
@@ -97,6 +101,7 @@ enum
   PROP_MAX_DAYS,
   PROP_TIME_ZONE,
   PROP_CHECK_INTERVAL,
+  PROP_VALID_TIMESPAN,
   PROP_NUM,
 };
 
@@ -139,7 +144,14 @@ static void     on_gclue_client_stop                       (GClueClient
 static void     gcal_weather_service_set_max_days          (GcalWeatherService  *self,
                                                             guint                days);
 
-static void     gcal_weather_service_update_weather        (GWeatherInfo        *info,
+static void     gcal_weather_service_set_valid_timespan    (GcalWeatherService *self,
+                                                            gint64              timespan);
+
+static void     gcal_weather_service_update_weather        (GcalWeatherService  *self,
+                                                            GWeatherInfo        *info,
+                                                            gboolean             reuse_old_on_error);
+
+static void     on_gweather_update                         (GWeatherInfo        *info,
                                                             GcalWeatherService  *self);
 
 /* Internal weather update timer API and callbacks */
@@ -234,6 +246,9 @@ gcal_weather_service_get_property (GObject    *object,
   case PROP_CHECK_INTERVAL:
     g_value_set_uint (value, gcal_weather_service_get_check_interval (self));
     break;
+  case PROP_VALID_TIMESPAN:
+    g_value_set_int64 (value, gcal_weather_service_get_valid_timespan (self));
+    break;
   default:
     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     break;
@@ -262,6 +277,9 @@ gcal_weather_service_set_property (GObject      *object,
   case PROP_CHECK_INTERVAL:
     gcal_weather_service_set_check_interval (self, g_value_get_uint (value));
     break;
+  case PROP_VALID_TIMESPAN:
+    gcal_weather_service_set_valid_timespan (self, g_value_get_int64 (value));
+    break;
   default:
     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     break;
@@ -281,7 +299,7 @@ gcal_weather_service_class_init (GcalWeatherServiceClass *klass)
   object_class->set_property = gcal_weather_service_set_property;
 
   /**
-   * GcalWeatherServiceClass:max-days:
+   * GcalWeatherService:max-days:
    *
    * Maximal number of days to fetch forecasts for.
    */
@@ -293,7 +311,7 @@ gcal_weather_service_class_init (GcalWeatherServiceClass *klass)
                           G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
   /**
-   * GcalWeatherServiceClass:time-zone:
+   * GcalWeatherService:time-zone:
    *
    * The time zone to use.
    */
@@ -304,7 +322,7 @@ gcal_weather_service_class_init (GcalWeatherServiceClass *klass)
                              G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
 
   /**
-   * GcalWeatherServiceClass:check-interval:
+   * GcalWeatherService:check-interval:
    *
    * Amount of seconds to wait before re-fetching weather infos.
    * Use %0 to disable timers.
@@ -317,6 +335,19 @@ gcal_weather_service_class_init (GcalWeatherServiceClass *klass)
                           G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
   /**
+   * GcalWeatherService:valid_timespan:
+   *
+   * Amount of seconds fetched weather information are considered as valid.
+   */
+  g_object_class_install_property
+      (G_OBJECT_CLASS (klass),
+       PROP_VALID_TIMESPAN,
+       g_param_spec_int64 ("valid-timespan", "valid-timespan", "valid-timespan",
+                           0, G_MAXINT64, 0,
+                           G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+
+  /**
    * GcalWeatherService::weather-changed:
    * @sender: The #GcalWeatherService
    * @self:   data pointer.
@@ -349,6 +380,8 @@ gcal_weather_service_init (GcalWeatherService *self)
   self->location_service_running = FALSE;
   self->location_service = NULL;
   self->weather_infos = NULL;
+  self->weather_infos_upated = -1;
+  self->valid_timespan = -1;
   self->gweather_info = NULL;
   self->weather_service_running = FALSE;
   self->max_days = 0;
@@ -869,7 +902,7 @@ gcal_weather_service_update_location (GcalWeatherService  *self,
   if (location == NULL)
     {
       g_debug ("Could not retrieve current location");
-      gcal_weather_service_update_weather (NULL, self);
+      gcal_weather_service_update_weather (self, NULL, FALSE);
     }
   else
     {
@@ -883,7 +916,16 @@ gcal_weather_service_update_location (GcalWeatherService  *self,
        * what is going on.
        */
       gweather_info_set_enabled_providers (self->gweather_info, GWEATHER_PROVIDER_METAR | 
GWEATHER_PROVIDER_OWM | GWEATHER_PROVIDER_YR_NO);
-      g_signal_connect (self->gweather_info, "updated", (GCallback) gcal_weather_service_update_weather, 
self);
+      g_signal_connect (self->gweather_info, "updated", (GCallback) on_gweather_update, self);
+
+      /* gweather_info_update might or might not trigger a
+       * GWeatherInfo::update() signal. Therefore, we have to
+       * remove weather information before querying new one.
+       * This might result in icon flickering on screen.
+       * We probably want to introduce a "unknown" or "loading"
+       * state in gweather-info to soften the effect.
+       */
+      gcal_weather_service_update_weather (self, NULL, FALSE);
       gweather_info_update (self->gweather_info);
 
       gcal_weather_service_timer_start (self);
@@ -1112,6 +1154,7 @@ gcal_weather_service_set_max_days (GcalWeatherService *self,
                                    guint               days)
 {
   g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
+  g_return_if_fail (days >= 1);
 
   self->max_days = days;
 
@@ -1120,23 +1163,42 @@ gcal_weather_service_set_max_days (GcalWeatherService *self,
 
 
 
+/* gcal_weather_service_set_valid_timespan:
+ * @self: A #GcalWeatherService instance.
+ * @timespan: Amount of seconds we consider weather information as valid.
+ */
+static void
+gcal_weather_service_set_valid_timespan (GcalWeatherService *self,
+                                         gint64              timespan)
+{
+  g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
+  g_return_if_fail (timespan >= 0);
+
+  self->valid_timespan = timespan;
+
+  g_object_notify ((GObject*) self, "valid-timespan");
+}
+
+
 /* gcal_weather_service_update_weather:
- * @info: (nullable): Newly received weather information or %NULL.
  * @self: A #GcalWeatherService instance.
+ * @info: (nullable): Newly received weather information or %NULL.
+ * @reuse_old_on_error: Whether to re-use old but not outdated weather
+ *                      information in case we could not fetch new data.
  *
  * Retrieves weather information for @location and triggers
  * #GcalWeatherService::weather-changed.
  */
 static void
-gcal_weather_service_update_weather (GWeatherInfo       *info,
-                                     GcalWeatherService *self)
+gcal_weather_service_update_weather (GcalWeatherService *self,
+                                     GWeatherInfo       *info,
+                                     gboolean            reuse_old_on_error)
 {
-  g_return_if_fail (info == NULL || GWEATHER_IS_INFO (info));
+  GSList *gwforecast = NULL; /* unowned */
+
   g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
+  g_return_if_fail (info == NULL || GWEATHER_IS_INFO (info));
 
-  /* Free previously received weather infos */
-  g_slist_free_full (self->weather_infos, g_object_unref);
-  self->weather_infos = NULL;
 
   /* Compute a list of newly received weather infos. */
   if (info == NULL)
@@ -1145,12 +1207,8 @@ gcal_weather_service_update_weather (GWeatherInfo       *info,
     }
   else if (gweather_info_is_valid (info))
     {
-      GSList *gwforecast; /* unowned */
-
       g_debug ("Received valid weather information");
-
       gwforecast = gweather_info_get_forecast_list (info);
-      self->weather_infos = preprocess_gweather_reports (self, gwforecast);
     }
   else
     {
@@ -1158,7 +1216,48 @@ gcal_weather_service_update_weather (GWeatherInfo       *info,
       g_debug ("Could not retrieve valid weather for location '%s'", location_name);
     }
 
-  g_signal_emit (self, gcal_weather_service_signals[SIG_WEATHER_CHANGED], 0);
+  if (gwforecast == NULL && self->weather_infos_upated >= 0)
+    {
+      gint64 now = g_get_monotonic_time ();
+      gboolean in_valid_timespan;
+
+      in_valid_timespan = (now - self->weather_infos_upated) / 1000000 <= self->valid_timespan;
+      if (!reuse_old_on_error || !in_valid_timespan)
+        {
+          g_slist_free_full (self->weather_infos, g_object_unref);
+          self->weather_infos = NULL;
+          self->weather_infos_upated = -1;
+
+          g_signal_emit (self, gcal_weather_service_signals[SIG_WEATHER_CHANGED], 0);
+        }
+    }
+  else if (gwforecast != NULL)
+    {
+      g_slist_free_full (self->weather_infos, g_object_unref);
+      self->weather_infos = preprocess_gweather_reports (self, gwforecast);
+      self->weather_infos_upated = g_get_monotonic_time ();
+
+      g_signal_emit (self, gcal_weather_service_signals[SIG_WEATHER_CHANGED], 0);
+    }
+}
+
+
+
+/* on_gweather_update:
+ * @self: A #GcalWeatherService instance.
+ * @timespan: Amount of seconds we consider weather information as valid.
+ *
+ * Triggered on weather updates with previously handled or no
+ * location changes.
+ */
+static void
+on_gweather_update (GWeatherInfo       *info,
+                    GcalWeatherService *self)
+{
+  g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
+  g_return_if_fail (info == NULL || GWEATHER_IS_INFO (info));
+
+  gcal_weather_service_update_weather (self, info, TRUE);
 }
 
 
@@ -1270,12 +1369,14 @@ on_timer_timeout (GcalWeatherService *self)
 GcalWeatherService *
 gcal_weather_service_new (GTimeZone *time_zone,
                           guint      max_days,
-                          guint      check_interval)
+                          guint      check_interval,
+                          gint64     valid_timespan)
 {
   return g_object_new (GCAL_TYPE_WEATHER_SERVICE,
                        "time-zone", time_zone,
                        "max-days", max_days,
                        "check-interval", check_interval,
+                       "valid-timespan", valid_timespan,
                        NULL);
 }
 
@@ -1388,6 +1489,21 @@ gcal_weather_service_get_max_days (GcalWeatherService *self)
 
 
 /**
+ * gcal_weather_service_get_valid_timespan:
+ * @self: The #GcalWeatherService instance.
+ *
+ * Getter for #GcalWeatherService:valid-interval.
+ */
+gint64
+gcal_weather_service_get_valid_timespan (GcalWeatherService *self)
+{
+  g_return_val_if_fail (GCAL_IS_WEATHER_SERVICE (self), 0);
+  return self->valid_timespan;
+}
+
+
+
+/**
  * gcal_weather_service_get_time_zone:
  * @self: The #GcalWeatherService instance.
  *
diff --git a/src/gcal-weather-service.h b/src/gcal-weather-service.h
index f0ecf664..693b8a6c 100644
--- a/src/gcal-weather-service.h
+++ b/src/gcal-weather-service.h
@@ -34,7 +34,8 @@ G_DECLARE_FINAL_TYPE (GcalWeatherService, gcal_weather_service, GCAL, WEATHER_SE
 
 GcalWeatherService* gcal_weather_service_new                (GTimeZone          *time_zone,
                                                              guint               max_days,
-                                                             guint               check_interval);
+                                                             guint               check_interval,
+                                                             gint64              valid_timespan);
 
 GTimeZone*          gcal_weather_service_get_time_zone      (GcalWeatherService *self);
 
@@ -48,6 +49,8 @@ void                gcal_weather_service_stop               (GcalWeatherService
 
 guint               gcal_weather_service_get_max_days       (GcalWeatherService *self);
 
+gint64              gcal_weather_service_get_valid_timespan (GcalWeatherService *self);
+
 guint               gcal_weather_service_get_check_interval (GcalWeatherService *self);
 
 GSList*             gcal_weather_service_get_weather_infos  (GcalWeatherService *self);


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