weather: Re-use old weather predictions on errors

This commit is contained in:
Florian Brosch
2017-12-11 21:32:00 -02:00
committed by Georges Basile Stavracas Neto
parent eb3e4a4078
commit d1ee082142
3 changed files with 144 additions and 23 deletions
+4 -2
View File
@@ -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);
}
+136 -20
View File
@@ -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.
@@ -316,6 +334,19 @@ gcal_weather_service_class_init (GcalWeatherServiceClass *klass)
0, G_MAXUINT, 0,
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
@@ -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_update_weather:
* @info: (nullable): Newly received weather information or %NULL.
/* 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:
* @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)
{
GSList *gwforecast = NULL; /* unowned */
g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
g_return_if_fail (info == NULL || GWEATHER_IS_INFO (info));
g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
/* 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);
}
@@ -1387,6 +1488,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.
+4 -1
View File
@@ -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);