2
Tracks which menu items get used by users and works to promote those
3
higher in the search rankings than others.
5
Copyright 2011 Canonical Ltd.
8
Ted Gould <ted@canonical.com>
10
This program is free software: you can redistribute it and/or modify it
11
under the terms of the GNU General Public License version 3, as published
12
by the Free Software Foundation.
14
This program is distributed in the hope that it will be useful, but
15
WITHOUT ANY WARRANTY; without even the implied warranties of
16
MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
17
PURPOSE. See the GNU General Public License for more details.
19
You should have received a copy of the GNU General Public License along
20
with this program. If not, see <http://www.gnu.org/licenses/>.
28
#include <glib/gstdio.h>
30
#include "usage-tracker.h"
31
#include "load-app-info.h"
33
struct _UsageTrackerPrivate {
39
#define USAGE_TRACKER_GET_PRIVATE(o) \
40
(G_TYPE_INSTANCE_GET_PRIVATE ((o), USAGE_TRACKER_TYPE, UsageTrackerPrivate))
42
static void usage_tracker_class_init (UsageTrackerClass *klass);
43
static void usage_tracker_init (UsageTracker *self);
44
static void usage_tracker_dispose (GObject *object);
45
static void usage_tracker_finalize (GObject *object);
46
static void build_db (UsageTracker * self);
47
static gboolean drop_entries (gpointer user_data);
48
static void check_app_init (UsageTracker * self, const gchar * application);
50
G_DEFINE_TYPE (UsageTracker, usage_tracker, G_TYPE_OBJECT);
53
usage_tracker_class_init (UsageTrackerClass *klass)
55
GObjectClass *object_class = G_OBJECT_CLASS (klass);
57
g_type_class_add_private (klass, sizeof (UsageTrackerPrivate));
59
object_class->dispose = usage_tracker_dispose;
60
object_class->finalize = usage_tracker_finalize;
66
usage_tracker_init (UsageTracker *self)
68
self->priv = USAGE_TRACKER_GET_PRIVATE(self);
70
self->priv->cachefile = NULL;
71
self->priv->db = NULL;
72
self->priv->drop_timer = 0;
74
const gchar * basecachedir = g_getenv("HUD_CACHE_DIR");
75
if (basecachedir == NULL) {
76
basecachedir = g_get_user_cache_dir();
79
gchar * cachedir = g_build_filename(basecachedir, "hud", NULL);
80
if (!g_file_test(cachedir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
81
g_mkdir(cachedir, 1 << 6 | 1 << 7 | 1 << 8); // 700
85
self->priv->cachefile = g_build_filename(basecachedir, "hud", "usage-log.sqlite", NULL);
86
gboolean db_exists = g_file_test(self->priv->cachefile, G_FILE_TEST_EXISTS);
87
int open_status = sqlite3_open(self->priv->cachefile, &self->priv->db);
89
if (open_status != SQLITE_OK) {
90
g_warning("Error building LRU DB");
91
sqlite3_close(self->priv->db);
92
self->priv->db = NULL;
95
if (self->priv->db != NULL && !db_exists) {
100
/* Drop entries daily if we run for a really long time */
101
self->priv->drop_timer = g_timeout_add_seconds(24 * 60 * 60, drop_entries, self);
107
usage_tracker_dispose (GObject *object)
109
UsageTracker * self = USAGE_TRACKER(object);
111
if (self->priv->db != NULL) {
112
sqlite3_close(self->priv->db);
113
self->priv->db = NULL;
116
if (self->priv->drop_timer != 0) {
117
g_source_remove(self->priv->drop_timer);
118
self->priv->drop_timer = 0;
121
G_OBJECT_CLASS (usage_tracker_parent_class)->dispose (object);
126
usage_tracker_finalize (GObject *object)
128
UsageTracker * self = USAGE_TRACKER(object);
130
if (self->priv->cachefile != NULL) {
131
g_free(self->priv->cachefile);
132
self->priv->cachefile = NULL;
135
G_OBJECT_CLASS (usage_tracker_parent_class)->finalize (object);
140
usage_tracker_new (void)
142
return g_object_new(USAGE_TRACKER_TYPE, NULL);
146
build_db (UsageTracker * self)
148
g_debug("New database, initializing");
150
/* Create the table */
151
int exec_status = SQLITE_OK;
152
gchar * failstring = NULL;
153
exec_status = sqlite3_exec(self->priv->db,
154
"create table usage (application text, entry text, timestamp datetime);",
155
NULL, NULL, &failstring);
156
if (exec_status != SQLITE_OK) {
157
g_warning("Unable to create table: %s", failstring);
160
/* Import data from the system */
166
usage_tracker_mark_usage (UsageTracker * self, const gchar * application, const gchar * entry)
168
g_return_if_fail(IS_USAGE_TRACKER(self));
169
check_app_init(self, application);
171
gchar * statement = g_strdup_printf("insert into usage (application, entry, timestamp) values ('%s', '%s', date('now'));", application, entry);
172
// g_debug("Executing: %s", statement);
174
int exec_status = SQLITE_OK;
175
gchar * failstring = NULL;
176
exec_status = sqlite3_exec(self->priv->db,
178
NULL, NULL, &failstring);
179
if (exec_status != SQLITE_OK) {
180
g_warning("Unable to insert into table: %s", failstring);
188
count_cb (void * user_data, int columns, char ** values, char ** names)
190
g_return_val_if_fail(columns == 1, -1);
192
guint * count = (guint *)user_data;
194
*count = g_ascii_strtoull(values[0], NULL, 10);
200
usage_tracker_get_usage (UsageTracker * self, const gchar * application, const gchar * entry)
202
g_return_val_if_fail(IS_USAGE_TRACKER(self), 0);
203
check_app_init(self, application);
205
gchar * statement = g_strdup_printf("select count(*) from usage where application = '%s' and entry = '%s' and timestamp > date('now', 'utc', '-30 days');", application, entry); // TODO: Add timestamp
206
// g_debug("Executing: %s", statement);
208
int exec_status = SQLITE_OK;
209
gchar * failstring = NULL;
211
exec_status = sqlite3_exec(self->priv->db,
213
count_cb, &count, &failstring);
214
if (exec_status != SQLITE_OK) {
215
g_warning("Unable to insert into table: %s", failstring);
223
drop_entries (gpointer user_data)
225
g_return_val_if_fail(IS_USAGE_TRACKER(user_data), FALSE);
226
UsageTracker * self = USAGE_TRACKER(user_data);
228
const gchar * statement = "delete from usage where timestamp < date('now', 'utc', '-30 days');";
229
// g_debug("Executing: %s", statement);
231
int exec_status = SQLITE_OK;
232
gchar * failstring = NULL;
233
exec_status = sqlite3_exec(self->priv->db,
235
NULL, NULL, &failstring);
236
if (exec_status != SQLITE_OK) {
237
g_warning("Unable to drop entries from table: %s", failstring);
244
check_app_init (UsageTracker * self, const gchar * application)
246
gchar * statement = g_strdup_printf("select count(*) from usage where application = '%s';", application);
248
int exec_status = SQLITE_OK;
249
gchar * failstring = NULL;
251
exec_status = sqlite3_exec(self->priv->db,
253
count_cb, &count, &failstring);
254
if (exec_status != SQLITE_OK) {
255
g_warning("Unable to insert into table: %s", failstring);
264
g_debug("Initializing application: %s", application);
265
gchar * basename = g_path_get_basename(application);
267
gchar * app_info_path = NULL;
269
if (g_getenv("HUD_APP_INFO_DIR") != NULL) {
270
app_info_path = g_strdup(g_getenv("HUD_APP_INFO_DIR"));
272
app_info_path = g_build_filename(DATADIR, "hud", "app-info", NULL);
275
gchar * app_info_filename = g_strdup_printf("%s.hud-app-info", basename);
276
gchar * app_info = g_build_filename(app_info_path, app_info_filename, NULL);
278
if (!load_app_info(app_info, self->priv->db)) {
279
g_warning("Unable to load application information for application '%s' at path '%s'", application, app_info);
283
g_free(app_info_filename);
284
g_free(app_info_path);