~jbicha/hud/build-depend-on-valac-not-gir

« back to all changes in this revision

Viewing changes to service/usage-tracker.c

Merging the HUD into indicator-appmenu

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
Tracks which menu items get used by users and works to promote those
 
3
higher in the search rankings than others.
 
4
 
 
5
Copyright 2011 Canonical Ltd.
 
6
 
 
7
Authors:
 
8
    Ted Gould <ted@canonical.com>
 
9
 
 
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.
 
13
 
 
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.
 
18
 
 
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/>.
 
21
*/
 
22
 
 
23
#ifdef HAVE_CONFIG_H
 
24
#include "config.h"
 
25
#endif
 
26
 
 
27
#include <glib.h>
 
28
#include <glib/gstdio.h>
 
29
#include <sqlite3.h>
 
30
#include "usage-tracker.h"
 
31
#include "load-app-info.h"
 
32
 
 
33
struct _UsageTrackerPrivate {
 
34
        gchar * cachefile;
 
35
        sqlite3 * db;
 
36
        guint drop_timer;
 
37
};
 
38
 
 
39
#define USAGE_TRACKER_GET_PRIVATE(o) \
 
40
(G_TYPE_INSTANCE_GET_PRIVATE ((o), USAGE_TRACKER_TYPE, UsageTrackerPrivate))
 
41
 
 
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);
 
49
 
 
50
G_DEFINE_TYPE (UsageTracker, usage_tracker, G_TYPE_OBJECT);
 
51
 
 
52
static void
 
53
usage_tracker_class_init (UsageTrackerClass *klass)
 
54
{
 
55
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
56
 
 
57
        g_type_class_add_private (klass, sizeof (UsageTrackerPrivate));
 
58
 
 
59
        object_class->dispose = usage_tracker_dispose;
 
60
        object_class->finalize = usage_tracker_finalize;
 
61
 
 
62
        return;
 
63
}
 
64
 
 
65
static void
 
66
usage_tracker_init (UsageTracker *self)
 
67
{
 
68
        self->priv = USAGE_TRACKER_GET_PRIVATE(self);
 
69
 
 
70
        self->priv->cachefile = NULL;
 
71
        self->priv->db = NULL;
 
72
        self->priv->drop_timer = 0;
 
73
 
 
74
        const gchar * basecachedir = g_getenv("HUD_CACHE_DIR");
 
75
        if (basecachedir == NULL) {
 
76
                basecachedir = g_get_user_cache_dir();
 
77
        }
 
78
 
 
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
 
82
        }
 
83
        g_free(cachedir);
 
84
 
 
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); 
 
88
 
 
89
        if (open_status != SQLITE_OK) {
 
90
                g_warning("Error building LRU DB");
 
91
                sqlite3_close(self->priv->db);
 
92
                self->priv->db = NULL;
 
93
        }
 
94
 
 
95
        if (self->priv->db != NULL && !db_exists) {
 
96
                build_db(self);
 
97
        }
 
98
 
 
99
        drop_entries(self);
 
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);
 
102
        
 
103
        return;
 
104
}
 
105
 
 
106
static void
 
107
usage_tracker_dispose (GObject *object)
 
108
{
 
109
        UsageTracker * self = USAGE_TRACKER(object);
 
110
 
 
111
        if (self->priv->db != NULL) {
 
112
                sqlite3_close(self->priv->db);
 
113
                self->priv->db = NULL;
 
114
        }
 
115
 
 
116
        if (self->priv->drop_timer != 0) {
 
117
                g_source_remove(self->priv->drop_timer);
 
118
                self->priv->drop_timer = 0;
 
119
        }
 
120
 
 
121
        G_OBJECT_CLASS (usage_tracker_parent_class)->dispose (object);
 
122
        return;
 
123
}
 
124
 
 
125
static void
 
126
usage_tracker_finalize (GObject *object)
 
127
{
 
128
        UsageTracker * self = USAGE_TRACKER(object);
 
129
 
 
130
        if (self->priv->cachefile != NULL) {
 
131
                g_free(self->priv->cachefile);
 
132
                self->priv->cachefile = NULL;
 
133
        }
 
134
 
 
135
        G_OBJECT_CLASS (usage_tracker_parent_class)->finalize (object);
 
136
        return;
 
137
}
 
138
 
 
139
UsageTracker *
 
140
usage_tracker_new (void)
 
141
{
 
142
        return g_object_new(USAGE_TRACKER_TYPE, NULL);
 
143
}
 
144
 
 
145
static void
 
146
build_db (UsageTracker * self)
 
147
{
 
148
        g_debug("New database, initializing");
 
149
 
 
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);
 
158
        }
 
159
 
 
160
        /* Import data from the system */
 
161
 
 
162
        return;
 
163
}
 
164
 
 
165
void
 
166
usage_tracker_mark_usage (UsageTracker * self, const gchar * application, const gchar * entry)
 
167
{
 
168
        g_return_if_fail(IS_USAGE_TRACKER(self));
 
169
        check_app_init(self, application);
 
170
 
 
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);
 
173
 
 
174
        int exec_status = SQLITE_OK;
 
175
        gchar * failstring = NULL;
 
176
        exec_status = sqlite3_exec(self->priv->db,
 
177
                                   statement,
 
178
                                   NULL, NULL, &failstring);
 
179
        if (exec_status != SQLITE_OK) {
 
180
                g_warning("Unable to insert into table: %s", failstring);
 
181
        }
 
182
 
 
183
        g_free(statement);
 
184
        return;
 
185
}
 
186
 
 
187
static int
 
188
count_cb (void * user_data, int columns, char ** values, char ** names)
 
189
{
 
190
        g_return_val_if_fail(columns == 1, -1);
 
191
 
 
192
        guint * count = (guint *)user_data;
 
193
 
 
194
        *count = g_ascii_strtoull(values[0], NULL, 10);
 
195
 
 
196
        return SQLITE_OK;
 
197
}
 
198
 
 
199
guint
 
200
usage_tracker_get_usage (UsageTracker * self, const gchar * application, const gchar * entry)
 
201
{
 
202
        g_return_val_if_fail(IS_USAGE_TRACKER(self), 0);
 
203
        check_app_init(self, application);
 
204
 
 
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);
 
207
 
 
208
        int exec_status = SQLITE_OK;
 
209
        gchar * failstring = NULL;
 
210
        guint count;
 
211
        exec_status = sqlite3_exec(self->priv->db,
 
212
                                   statement,
 
213
                                   count_cb, &count, &failstring);
 
214
        if (exec_status != SQLITE_OK) {
 
215
                g_warning("Unable to insert into table: %s", failstring);
 
216
        }
 
217
 
 
218
        g_free(statement);
 
219
        return count;
 
220
}
 
221
 
 
222
static gboolean
 
223
drop_entries (gpointer user_data)
 
224
{
 
225
        g_return_val_if_fail(IS_USAGE_TRACKER(user_data), FALSE);
 
226
        UsageTracker * self = USAGE_TRACKER(user_data);
 
227
 
 
228
        const gchar * statement = "delete from usage where timestamp < date('now', 'utc', '-30 days');";
 
229
        // g_debug("Executing: %s", statement);
 
230
 
 
231
        int exec_status = SQLITE_OK;
 
232
        gchar * failstring = NULL;
 
233
        exec_status = sqlite3_exec(self->priv->db,
 
234
                                   statement,
 
235
                                   NULL, NULL, &failstring);
 
236
        if (exec_status != SQLITE_OK) {
 
237
                g_warning("Unable to drop entries from table: %s", failstring);
 
238
        }
 
239
 
 
240
        return TRUE;
 
241
}
 
242
 
 
243
static void
 
244
check_app_init (UsageTracker * self, const gchar * application)
 
245
{
 
246
        gchar * statement = g_strdup_printf("select count(*) from usage where application = '%s';", application);
 
247
 
 
248
        int exec_status = SQLITE_OK;
 
249
        gchar * failstring = NULL;
 
250
        guint count = 0;
 
251
        exec_status = sqlite3_exec(self->priv->db,
 
252
                                   statement,
 
253
                                   count_cb, &count, &failstring);
 
254
        if (exec_status != SQLITE_OK) {
 
255
                g_warning("Unable to insert into table: %s", failstring);
 
256
        }
 
257
 
 
258
        g_free(statement);
 
259
 
 
260
        if (count > 0) {
 
261
                return;
 
262
        }
 
263
 
 
264
        g_debug("Initializing application: %s", application);
 
265
        gchar * basename = g_path_get_basename(application);
 
266
 
 
267
        gchar * app_info_path = NULL;
 
268
 
 
269
        if (g_getenv("HUD_APP_INFO_DIR") != NULL) {
 
270
                app_info_path = g_strdup(g_getenv("HUD_APP_INFO_DIR"));
 
271
        } else {
 
272
                app_info_path = g_build_filename(DATADIR, "hud", "app-info", NULL);
 
273
        }
 
274
 
 
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);
 
277
 
 
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);
 
280
        }
 
281
 
 
282
        g_free(app_info);
 
283
        g_free(app_info_filename);
 
284
        g_free(app_info_path);
 
285
        g_free(basename);
 
286
 
 
287
        return;
 
288
}