~stolowski/unity-scope-click/populate-departments-db

« back to all changes in this revision

Viewing changes to tools/init-departments/init-departments.cpp

Merged departments init tool.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2014 Canonical Ltd.
 
3
 *
 
4
 * This program is free software: you can redistribute it and/or modify it
 
5
 * under the terms of the GNU General Public License version 3, as published
 
6
 * by the Free Software Foundation.
 
7
 *
 
8
 * This program is distributed in the hope that it will be useful, but
 
9
 * WITHOUT ANY WARRANTY; without even the implied warranties of
 
10
 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 
11
 * PURPOSE.  See the GNU General Public License for more details.
 
12
 *
 
13
 * You should have received a copy of the GNU General Public License along
 
14
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 
15
 *
 
16
 * In addition, as a special exception, the copyright holders give
 
17
 * permission to link the code of portions of this program with the
 
18
 * OpenSSL library under certain conditions as described in each
 
19
 * individual source file, and distribute linked combinations
 
20
 * including the two.
 
21
 * You must obey the GNU General Public License in all respects
 
22
 * for all of the code used other than OpenSSL.  If you modify
 
23
 * file(s) with this exception, you may extend this exception to your
 
24
 * version of the file(s), but you are not obligated to do so.  If you
 
25
 * do not wish to do so, delete this exception statement from your
 
26
 * version.  If you delete this exception statement from all source
 
27
 * files in the program, then also delete it here.
 
28
 */
 
29
 
 
30
#include <click/interface.h>
 
31
#include <click/key_file_locator.h>
 
32
#include <click/index.h>
 
33
#include <click/webclient.h>
 
34
#include <click/network_access_manager.h>
 
35
#include <click/qtbridge.h>
 
36
#include <click/departments-db.h>
 
37
#include <future>
 
38
#include <iostream>
 
39
#include <QDebug>
 
40
#include <QtGlobal>
 
41
 
 
42
enum
 
43
{
 
44
    // invalid arguments
 
45
    DEPTS_ERROR_ARG = 1,
 
46
 
 
47
    // network request errors
 
48
    DEPTS_ERROR_NETWORK = 2,
 
49
 
 
50
    // sqlite db errors
 
51
    DEPTS_ERROR_DB = 5,
 
52
 
 
53
    // click errors
 
54
    DEPTS_ERROR_CLICK_PARSE = 10,
 
55
    DEPTS_ERROR_CLICK_CALL = 11,
 
56
    DEPTS_ERROR_CLICK_UNKNOWN = 12
 
57
};
 
58
 
 
59
std::list<std::pair<std::string, std::string>> NON_CLICK_APPS = {
 
60
    {"address-book-app.desktop", "accessories"},
 
61
    {"dialer-app.desktop", "accessories"},
 
62
    {"mediaplayer-app.desktop", "sound-video"},
 
63
    {"messaging-app.desktop", "accessories"},
 
64
    {"ubuntu-system-settings.desktop", "accessories"},
 
65
    {"webbrowser-app.desktop", "web-browsers"}
 
66
};
 
67
 
 
68
QSharedPointer<click::KeyFileLocator> keyFileLocator(new click::KeyFileLocator());
 
69
click::Interface iface(keyFileLocator);
 
70
 
 
71
void noDebug(QtMsgType, const QMessageLogContext&, const QString&) {}
 
72
 
 
73
int main(int argc, char **argv)
 
74
{
 
75
    if (argc < 3)
 
76
    {
 
77
        std::cerr << "Usage: " << argv[0] << " DBFILE LOCALE1 [LOCALE2 ...]" << std::endl;
 
78
        return DEPTS_ERROR_ARG;
 
79
    }
 
80
 
 
81
    const std::string dbfile(argv[1]);
 
82
    const std::set<std::string> locales(argv + 2, argv + argc);
 
83
 
 
84
    if (!getenv("INIT_DEPARTMENTS_DEBUG"))
 
85
        qInstallMessageHandler(noDebug);
 
86
 
 
87
    std::unique_ptr<click::DepartmentsDb> db;
 
88
    try
 
89
    {
 
90
        db.reset(new click::DepartmentsDb(dbfile));
 
91
    }
 
92
    catch (const std::exception &e)
 
93
    {
 
94
        std::cerr << "Failed to open departments database " << dbfile << std::endl;
 
95
        return DEPTS_ERROR_DB;
 
96
    }
 
97
 
 
98
    auto nam = QSharedPointer<click::network::AccessManager>(new click::network::AccessManager());
 
99
    auto client = QSharedPointer<click::web::Client>(new click::web::Client(nam));
 
100
    click::Index index(client);
 
101
    std::vector<click::web::Cancellable> cnc;
 
102
 
 
103
    std::promise<void> qt_ready;
 
104
    std::promise<void> bootstrap_ready;
 
105
    std::promise<click::PackageSet> pkgs_ready;
 
106
    auto qt_ready_ft = qt_ready.get_future();
 
107
    auto bootstrap_ft = bootstrap_ready.get_future();
 
108
    auto pkgs_ft = pkgs_ready.get_future();
 
109
 
 
110
    std::atomic<int> return_val(0);
 
111
    std::atomic<std::vector<std::string>::size_type> num_of_locales(locales.size());
 
112
    std::atomic<click::PackageSet::size_type> num_of_pkgs(0);
 
113
 
 
114
    //
 
115
    // a thread that does bootstrap request
 
116
    std::thread net_thread([&]() {
 
117
        qt_ready_ft.get();
 
118
 
 
119
        for (auto const locale: locales)
 
120
        {
 
121
            qt::core::world::enter_with_task([&return_val, &bootstrap_ready, &index, &cnc, &num_of_locales, &db, locale]() {
 
122
 
 
123
                std::cout << "Getting departments for locale '" << locale << "'" << std::endl;
 
124
                cnc.push_back(index.bootstrap([&return_val, &bootstrap_ready, &num_of_locales, &db, locale](const click::DepartmentList& depts, const click::HighlightList&, click::Index::Error error, int) {
 
125
                    std::cout << "Bootstrap call for locale '" << locale << "' finished" << std::endl;
 
126
 
 
127
                    if (error == click::Index::Error::NoError)
 
128
                    {
 
129
                        try
 
130
                        {
 
131
                            db->store_departments(depts, locale);
 
132
                            std::cout << "Stored departments for locale '" << locale << "'" << std::endl;
 
133
                        }
 
134
                        catch (const std::exception& e)
 
135
                        {
 
136
                            std::cerr << "Failed to update departments database: " << e.what() << std::endl;
 
137
                            return_val = DEPTS_ERROR_DB;
 
138
                        }
 
139
 
 
140
                        if (--num_of_locales == 0)
 
141
                        {
 
142
                            std::cout << "All locales processed" << std::endl;
 
143
                            bootstrap_ready.set_value();
 
144
                        }
 
145
                    }
 
146
                    else
 
147
                    {
 
148
                        std::cerr << "Network error" << std::endl;
 
149
                        return_val = DEPTS_ERROR_NETWORK;
 
150
                        bootstrap_ready.set_value();
 
151
                        qt::core::world::destroy();
 
152
                        return;
 
153
                    }
 
154
                }));
 
155
            });
 
156
        }
 
157
    });
 
158
 
 
159
    //
 
160
    // a thread that iterates over all packages and performs details requests
 
161
    // it initially blocks on bootstrap_ft and pkgs_ft future
 
162
    // (waits until bootstrap finished and main thread gets all the click packages)
 
163
    std::thread details_thread([&]() {
 
164
        bootstrap_ft.get();
 
165
        auto const pkgs = pkgs_ft.get();
 
166
 
 
167
        num_of_pkgs = pkgs.size();
 
168
 
 
169
        // if click list failed or nothing to do, then end this thread and stop Qt bridge
 
170
        if (return_val != 0 || num_of_pkgs == 0)
 
171
        {
 
172
            std::cerr << "No packages to process or an error occurred, not fetching departments" << std::endl;
 
173
            qt::core::world::destroy();
 
174
            return;
 
175
        }
 
176
 
 
177
            std::cout << "Getting package details for " << num_of_pkgs << " packages" << std::endl;
 
178
 
 
179
            //
 
180
            // note: this queues requests for all the packages; the number of packages to process
 
181
            // is kept in num_of_pkgs which gets decreased, the last processed package will stop Qt bridge.
 
182
            for (auto const& pkg: pkgs)
 
183
            {
 
184
                auto const pkgname = pkg.name;
 
185
 
 
186
                qt::core::world::enter_with_task([&return_val, &index, &cnc, &num_of_pkgs, pkgname, &db]() {
 
187
 
 
188
                    cnc.push_back(index.get_details(pkgname, [&return_val, &num_of_pkgs, pkgname, &db](const click::PackageDetails& details, click::Index::Error error) {
 
189
                        std::cout << "Details call for " << pkgname << " finished" << std::endl;
 
190
 
 
191
                        if (error == click::Index::Error::NoError)
 
192
                        {
 
193
                            try
 
194
                            {
 
195
                                std::cout << "Storing package department for " << pkgname << ", " << details.department << std::endl;
 
196
                                db->store_package_mapping(pkgname, details.department);
 
197
                            }
 
198
                            catch (const std::exception& e)
 
199
                            {
 
200
                                std::cerr << "Failed to update departments database: " << e.what() << std::endl;
 
201
                                return_val = DEPTS_ERROR_DB;
 
202
                            }
 
203
                        }
 
204
                        else
 
205
                        {
 
206
                            std::cerr << "Network error" << std::endl;
 
207
                            return_val = DEPTS_ERROR_NETWORK;
 
208
                        }
 
209
                        if (--num_of_pkgs == 0)
 
210
                        {
 
211
                            std::cout << "All packages processed" << std::endl;
 
212
                            qt::core::world::destroy();
 
213
                        }
 
214
                    }));
 
215
                });
 
216
            }
 
217
    });
 
218
 
 
219
    //
 
220
    // enter Qt world; this blocks until qt::core:;world::destroy() gets called
 
221
    qt::core::world::build_and_run(argc, argv, [&pkgs_ready, &qt_ready, &return_val]() {
 
222
 
 
223
        qt::core::world::enter_with_task([&return_val, &pkgs_ready, &qt_ready]() {
 
224
            std::cout << "Querying click for installed packages" << std::endl;
 
225
            iface.get_installed_packages([&return_val, &pkgs_ready, &qt_ready](click::PackageSet pkgs, click::InterfaceError error) {
 
226
                if (error == click::InterfaceError::NoError)
 
227
                {
 
228
                    std::cout << "Found: " << pkgs.size() << " click packages" << std::endl;
 
229
                }
 
230
                else
 
231
                {
 
232
                    if (error == click::InterfaceError::ParseError)
 
233
                    {
 
234
                        std::cerr << "Error parsing click output" << std::endl;
 
235
                        return_val = DEPTS_ERROR_CLICK_PARSE;
 
236
                    }
 
237
                    else if (error == click::InterfaceError::CallError)
 
238
                    {
 
239
                        std::cerr << "Error calling click command" << std::endl;
 
240
                        return_val = DEPTS_ERROR_CLICK_CALL;
 
241
                    }
 
242
                    else
 
243
                    {
 
244
                        std::cerr << "An unknown click error occured" << std::endl;
 
245
                        return_val = DEPTS_ERROR_CLICK_UNKNOWN;
 
246
                    }
 
247
                }
 
248
                qt_ready.set_value();
 
249
                pkgs_ready.set_value(pkgs); // this unblocks net_thread
 
250
            });
 
251
        });
 
252
    });
 
253
 
 
254
    net_thread.join();
 
255
    details_thread.join();
 
256
 
 
257
    try
 
258
    {
 
259
        for (auto const& app: NON_CLICK_APPS)
 
260
        {
 
261
            db->store_package_mapping(app.first, app.second);
 
262
        }
 
263
    }
 
264
    catch (const std::exception &e)
 
265
    {
 
266
        std::cerr << "Failed to insert non-click appsint database" << std::endl;
 
267
        return DEPTS_ERROR_DB;
 
268
    }
 
269
 
 
270
    std::cout << std::endl << "Summary:" << std::endl
 
271
        << "Number of department mappings: " << db->department_mapping_count() << std::endl
 
272
        << "Number of department names (all locales): " << db->department_name_count() << std::endl
 
273
        << "Number of applications: " << db->package_count() << std::endl;
 
274
 
 
275
    return return_val;
 
276
}