Merge lp:~stolowski/unity-scope-click/populate-departments-db into lp:unity-scope-click/devel

Proposed by Paweł Stołowski
Status: Merged
Approved by: Alejandro J. Cura
Approved revision: 326
Merged at revision: 334
Proposed branch: lp:~stolowski/unity-scope-click/populate-departments-db
Merge into: lp:unity-scope-click/devel
Prerequisite: lp:~stolowski/unity-scope-click/departments-db
Diff against target: 1444 lines (+616/-69)
26 files modified
CMakeLists.txt (+1/-0)
libclickscope/click/departments-db.cpp (+51/-1)
libclickscope/click/departments-db.h (+20/-13)
libclickscope/click/departments.cpp (+2/-1)
libclickscope/click/departments.h (+1/-0)
libclickscope/click/interface.h (+1/-0)
libclickscope/click/package.cpp (+9/-0)
libclickscope/click/package.h (+2/-0)
libclickscope/click/preview.cpp (+64/-20)
libclickscope/click/preview.h (+26/-8)
libclickscope/tests/CMakeLists.txt (+2/-1)
libclickscope/tests/fake_json.cpp (+9/-2)
libclickscope/tests/test_departments.cpp (+3/-3)
libclickscope/tests/test_index.cpp (+4/-2)
scope/clickapps/apps-scope.cpp (+1/-1)
scope/clickstore/CMakeLists.txt (+2/-1)
scope/clickstore/store-query.cpp (+31/-1)
scope/clickstore/store-query.h (+3/-1)
scope/clickstore/store-scope.cpp (+12/-2)
scope/clickstore/store-scope.h (+2/-0)
scope/tests/CMakeLists.txt (+3/-2)
scope/tests/test_query.cpp (+61/-10)
tools/CMakeLists.txt (+1/-0)
tools/init-departments/CMakeLists.txt (+19/-0)
tools/init-departments/README (+10/-0)
tools/init-departments/init-departments.cpp (+276/-0)
To merge this branch: bzr merge lp:~stolowski/unity-scope-click/populate-departments-db
Reviewer Review Type Date Requested Status
Alejandro J. Cura (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+225434@code.launchpad.net

Commit message

Populate departments database in the store scope. Use slugs for unique department ids when parsing. Implemented a tool that initializes or updates departments db.

Description of the change

Populate departments database in the store scope. Use slugs for unique department ids when parsing.

Implemented a tool that queries for locally installed packages, calls ubuntu store server for them and creates a sqlite db file with departments and package - department mapping data that will later be used by click apps scope.

Usage:
init-departments DBFILE LOCALE1 [LOCALE2 ...]

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
325. By Paweł Stołowski

Merged departments init tool.

326. By Paweł Stołowski

Updated README.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alejandro J. Cura (alecu) wrote :

Code looks good. I tested the debs from this and the following branches on the device, and it works beautifully.

One comment is that I would like to see tests for the init-departments tool, since it will be used to build images, and any problems there will lead to broken images. I propose to open a bug for that, and to work on such tests in an upcoming branch.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2014-07-11 14:46:09 +0000
3+++ CMakeLists.txt 2014-07-15 14:51:22 +0000
4@@ -54,6 +54,7 @@
5 add_subdirectory(scope)
6 add_subdirectory(data)
7 add_subdirectory(po)
8+add_subdirectory(tools)
9
10 include(EnableCoverageReport) # Using gcov CMake modules from https://code.cor-lab.org/projects/rsc
11 #####################################################################
12
13=== modified file 'libclickscope/click/departments-db.cpp'
14--- libclickscope/click/departments-db.cpp 2014-07-02 13:18:32 +0000
15+++ libclickscope/click/departments-db.cpp 2014-07-15 14:51:22 +0000
16@@ -45,7 +45,7 @@
17 if (!path.isEmpty())
18 {
19 QDir("/").mkpath(path);
20- const std::string dbpath = path.toStdString() + "/clickscope/click-departments.db";
21+ const std::string dbpath = path.toStdString() + "/click-departments.db";
22 return std::unique_ptr<DepartmentsDb>(new DepartmentsDb(dbpath));
23 }
24 throw std::runtime_error("Cannot determine cache directory");
25@@ -56,6 +56,8 @@
26 init_db(name);
27
28 delete_pkgmap_query_.reset(new QSqlQuery(db_));
29+ delete_depts_query_.reset(new QSqlQuery(db_));
30+ delete_deptnames_query_.reset(new QSqlQuery(db_));
31 insert_pkgmap_query_.reset(new QSqlQuery(db_));
32 insert_dept_id_query_.reset(new QSqlQuery(db_));
33 insert_dept_name_query_.reset(new QSqlQuery(db_));
34@@ -66,6 +68,8 @@
35 select_dept_name_.reset(new QSqlQuery(db_));
36
37 delete_pkgmap_query_->prepare("DELETE FROM pkgmap WHERE pkgid=:pkgid");
38+ delete_depts_query_->prepare("DELETE FROM depts");
39+ delete_deptnames_query_->prepare("DELETE FROM deptnames WHERE locale=:locale");
40 insert_pkgmap_query_->prepare("INSERT OR REPLACE INTO pkgmap (pkgid, deptid) VALUES (:pkgid, :deptid)");
41 insert_dept_id_query_->prepare("INSERT OR REPLACE INTO depts (deptid, parentid) VALUES (:deptid, :parentid)");
42 insert_dept_name_query_->prepare("INSERT OR REPLACE INTO deptnames (deptid, locale, name) VALUES (:deptid, :locale, :name)");
43@@ -76,6 +80,10 @@
44 select_dept_name_->prepare("SELECT name FROM deptnames WHERE deptid=:deptid AND locale=:locale");
45 }
46
47+DepartmentsDb::~DepartmentsDb()
48+{
49+}
50+
51 void DepartmentsDb::init_db(const std::string& name)
52 {
53 db_ = QSqlDatabase::addDatabase("QSQLITE");
54@@ -308,4 +316,46 @@
55 return q.value(0).toInt();
56 }
57
58+void DepartmentsDb::store_departments_(const click::DepartmentList& depts, const std::string& locale)
59+{
60+ for (auto const& dept: depts)
61+ {
62+ store_department_name(dept->id(), locale, dept->name());
63+ for (auto const& subdep: dept->sub_departments())
64+ {
65+ store_department_mapping(subdep->id(), dept->id());
66+ }
67+ store_departments_(dept->sub_departments(), locale);
68+ }
69+}
70+
71+void DepartmentsDb::store_departments(const click::DepartmentList& depts, const std::string& locale)
72+{
73+ if (!db_.transaction())
74+ {
75+ std::cerr << "Failed to start transaction" << std::endl;
76+ }
77+
78+ //
79+ // delete existing departments for given locale first
80+ delete_deptnames_query_->bindValue(":locale", QVariant(QString::fromStdString(locale)));
81+ if (!delete_deptnames_query_->exec())
82+ {
83+ db_.rollback();
84+ report_db_error(delete_deptnames_query_->lastError(), "Failed to delete from deptnames");
85+ }
86+ if (!delete_depts_query_->exec())
87+ {
88+ db_.rollback();
89+ report_db_error(delete_depts_query_->lastError(), "Failed to delete from depts");
90+ }
91+
92+ store_departments_(depts, locale);
93+
94+ if (!db_.commit())
95+ {
96+ std::cerr << "Failed to commit transaction" << std::endl;
97+ }
98+}
99+
100 }
101
102=== modified file 'libclickscope/click/departments-db.h'
103--- libclickscope/click/departments-db.h 2014-07-02 13:14:36 +0000
104+++ libclickscope/click/departments-db.h 2014-07-15 14:51:22 +0000
105@@ -30,6 +30,7 @@
106 #ifndef CLICK_DEPARTMENTS_DB_H
107 #define CLICK_DEPARTMENTS_DB_H
108
109+#include <click/departments.h>
110 #include <string>
111 #include <set>
112 #include <unordered_set>
113@@ -43,7 +44,7 @@
114 namespace click
115 {
116
117-class DepartmentsDb final
118+class DepartmentsDb
119 {
120 public:
121 struct DepartmentInfo
122@@ -61,29 +62,35 @@
123 DepartmentsDb(const std::string& name);
124 DepartmentsDb(const DepartmentsDb& other) = delete;
125 DepartmentsDb& operator=(const DepartmentsDb&) = delete;
126-
127- std::string get_department_name(const std::string& department_id, const std::list<std::string>& locales);
128- std::unordered_set<std::string> get_packages_for_department(const std::string& department_id, bool recursive = true);
129- std::string get_parent_department_id(const std::string& department_id);
130- std::list<DepartmentInfo> get_children_departments(const std::string& department_id);
131-
132- void store_package_mapping(const std::string& package_id, const std::string& department_id);
133- void store_department_mapping(const std::string& department_id, const std::string& parent_department_id);
134- void store_department_name(const std::string& department_id, const std::string& locale, const std::string& name);
135+ virtual ~DepartmentsDb();
136+
137+ virtual std::string get_department_name(const std::string& department_id, const std::list<std::string>& locales);
138+ virtual std::unordered_set<std::string> get_packages_for_department(const std::string& department_id, bool recursive = true);
139+ virtual std::string get_parent_department_id(const std::string& department_id);
140+ virtual std::list<DepartmentInfo> get_children_departments(const std::string& department_id);
141+
142+ virtual void store_package_mapping(const std::string& package_id, const std::string& department_id);
143+ virtual void store_department_mapping(const std::string& department_id, const std::string& parent_department_id);
144+ virtual void store_department_name(const std::string& department_id, const std::string& locale, const std::string& name);
145
146 // these methods are mostly for tests
147- int department_mapping_count() const;
148- int package_count() const;
149- int department_name_count() const;
150+ virtual int department_mapping_count() const;
151+ virtual int package_count() const;
152+ virtual int department_name_count() const;
153+
154+ void store_departments(const click::DepartmentList& depts, const std::string& locale);
155
156 static std::unique_ptr<DepartmentsDb> create_db();
157
158 private:
159 void init_db(const std::string& name);
160+ void store_departments_(const click::DepartmentList& depts, const std::string& locale);
161 static void report_db_error(const QSqlError& error, const std::string& message);
162
163 QSqlDatabase db_;
164 std::unique_ptr<QSqlQuery> delete_pkgmap_query_;
165+ std::unique_ptr<QSqlQuery> delete_depts_query_;
166+ std::unique_ptr<QSqlQuery> delete_deptnames_query_;
167 std::unique_ptr<QSqlQuery> insert_pkgmap_query_;
168 std::unique_ptr<QSqlQuery> insert_dept_id_query_;
169 std::unique_ptr<QSqlQuery> insert_dept_name_query_;
170
171=== modified file 'libclickscope/click/departments.cpp'
172--- libclickscope/click/departments.cpp 2014-06-23 10:12:52 +0000
173+++ libclickscope/click/departments.cpp 2014-07-15 14:51:22 +0000
174@@ -93,6 +93,7 @@
175 {
176 auto const item = node[i];
177
178+ auto slug = check_mandatory_attribute(item, Department::JsonKeys::slug, Json::ValueType::stringValue).asString();
179 auto name = check_mandatory_attribute(item, Department::JsonKeys::name, Json::ValueType::stringValue).asString();
180 const bool has_children = item.isMember(Department::JsonKeys::has_children) ? item[Department::JsonKeys::has_children].asBool() : false;
181
182@@ -100,7 +101,7 @@
183 auto const self = check_mandatory_attribute(links, Department::JsonKeys::self, Json::ValueType::objectValue);
184 auto const href = check_mandatory_attribute(self, Department::JsonKeys::href, Json::ValueType::stringValue).asString();
185
186- auto dep = std::make_shared<Department>(name, name, href, has_children); //FIXME: id
187+ auto dep = std::make_shared<Department>(slug, name, href, has_children);
188 if (item.isObject() && item.isMember(Department::JsonKeys::embedded))
189 {
190 auto const emb = item[Department::JsonKeys::embedded];
191
192=== modified file 'libclickscope/click/departments.h'
193--- libclickscope/click/departments.h 2014-06-23 10:12:52 +0000
194+++ libclickscope/click/departments.h 2014-07-15 14:51:22 +0000
195@@ -47,6 +47,7 @@
196 struct JsonKeys
197 {
198 JsonKeys() = delete;
199+ constexpr static const char* slug {"slug"};
200 constexpr static const char* name {"name"};
201 constexpr static const char* embedded {"_embedded"};
202 constexpr static const char* department {"clickindex:department"};
203
204=== modified file 'libclickscope/click/interface.h'
205--- libclickscope/click/interface.h 2014-06-16 15:38:49 +0000
206+++ libclickscope/click/interface.h 2014-07-15 14:51:22 +0000
207@@ -75,6 +75,7 @@
208
209 ManifestList manifest_list_from_json(const std::string& json);
210 Manifest manifest_from_json(const std::string& json);
211+PackageSet package_names_from_stdout(const std::string& stdout_data);
212
213 class Interface
214 {
215
216=== modified file 'libclickscope/click/package.cpp'
217--- libclickscope/click/package.cpp 2014-06-26 17:31:28 +0000
218+++ libclickscope/click/package.cpp 2014-07-15 14:51:22 +0000
219@@ -126,6 +126,15 @@
220 details.download_url = root[JsonKeys::download_url].asString();
221 details.license = root[JsonKeys::license].asString();
222
223+ if (root[JsonKeys::department].isArray())
224+ {
225+ auto const dept_array = root[JsonKeys::department];
226+ if (dept_array.size() > 0)
227+ {
228+ details.department = dept_array[dept_array.size() - 1].asString();
229+ }
230+ }
231+
232 // Optional details go here.
233 if (root[JsonKeys::version].isString())
234 details.version = root[JsonKeys::version].asString();
235
236=== modified file 'libclickscope/click/package.h'
237--- libclickscope/click/package.h 2014-06-26 17:31:28 +0000
238+++ libclickscope/click/package.h 2014-07-15 14:51:22 +0000
239@@ -126,6 +126,7 @@
240 constexpr static const char* terms_of_service{"terms_of_service"};
241 constexpr static const char* license{"license"};
242 constexpr static const char* publisher{"publisher"};
243+ constexpr static const char* department{"department"};
244 constexpr static const char* main_screenshot_url{"screenshot_url"};
245 constexpr static const char* more_screenshot_urls{"screenshot_urls"};
246 constexpr static const char* binary_filesize{"binary_filesize"};
247@@ -149,6 +150,7 @@
248 json::Value::UInt64 binary_filesize;
249 std::string version;
250 std::string framework;
251+ std::string department;
252 };
253
254 std::ostream& operator<<(std::ostream& out, const PackageDetails& details);
255
256=== modified file 'libclickscope/click/preview.cpp'
257--- libclickscope/click/preview.cpp 2014-07-11 08:31:53 +0000
258+++ libclickscope/click/preview.cpp 2014-07-15 14:51:22 +0000
259@@ -34,6 +34,7 @@
260 #include <click/download-manager.h>
261 #include <click/launcher.h>
262 #include <click/dbus_constants.h>
263+#include <click/departments-db.h>
264
265 #include <boost/algorithm/string/replace.hpp>
266
267@@ -53,21 +54,56 @@
268
269 namespace click {
270
271+DepartmentUpdater::DepartmentUpdater(const std::shared_ptr<click::DepartmentsDb>& depts)
272+ : depts(depts)
273+{
274+}
275+
276+void DepartmentUpdater::store_department(const PackageDetails& details)
277+{
278+ //
279+ // store package -> department mapping in sqlite db
280+ if (depts)
281+ {
282+ if (!details.department.empty())
283+ {
284+ try
285+ {
286+ depts->store_package_mapping(details.package.name, details.department);
287+ qDebug() << "Storing mapping for" << QString::fromStdString(details.package.name) << ":" << QString::fromStdString(details.department);
288+ }
289+ catch (const std::exception& e)
290+ {
291+ qWarning() << "Failed to store package mapping for package '"
292+ << QString::fromStdString(details.package.name)
293+ << "', department '" << QString::fromStdString(details.department)
294+ << "':" << QString::fromStdString(e.what());
295+ }
296+ }
297+ else
298+ {
299+ qWarning() << "Department is empty for package" << QString::fromStdString(details.package.name);
300+ }
301+ }
302+}
303+
304 // Preview base class
305
306 Preview::Preview(const unity::scopes::Result& result,
307 const unity::scopes::ActionMetadata& metadata,
308 const QSharedPointer<click::web::Client>& client,
309- const QSharedPointer<click::network::AccessManager>& nam)
310+ const QSharedPointer<click::network::AccessManager>& nam,
311+ std::shared_ptr<click::DepartmentsDb> depts)
312 : PreviewQueryBase(result, metadata)
313 {
314- strategy.reset(choose_strategy(result, metadata, client, nam));
315+ strategy.reset(choose_strategy(result, metadata, client, nam, depts));
316 }
317
318 PreviewStrategy* Preview::choose_strategy(const unity::scopes::Result &result,
319 const unity::scopes::ActionMetadata &metadata,
320 const QSharedPointer<web::Client> &client,
321- const QSharedPointer<click::network::AccessManager>& nam)
322+ const QSharedPointer<click::network::AccessManager>& nam,
323+ std::shared_ptr<click::DepartmentsDb> depts)
324 {
325 if (metadata.scope_data().which() != scopes::Variant::Type::Null) {
326 auto metadict = metadata.scope_data().get_dict();
327@@ -81,26 +117,26 @@
328 << " and close_preview="
329 << metadict.count(click::Preview::Actions::CLOSE_PREVIEW);
330
331- return new InstalledPreview(result, metadata, client);
332+ return new InstalledPreview(result, metadata, client, depts);
333 } else if (metadict.count("action_id") != 0 && metadict.count("download_url") != 0) {
334 std::string action_id = metadict["action_id"].get_string();
335 std::string download_url = metadict["download_url"].get_string();
336 if (action_id == click::Preview::Actions::INSTALL_CLICK) {
337- return new InstallingPreview(download_url, result, client, nam);
338+ return new InstallingPreview(download_url, result, client, nam, depts);
339 } else {
340 qWarning() << "unexpected action id " << QString::fromStdString(action_id)
341 << " given with download_url" << QString::fromStdString(download_url);
342- return new UninstalledPreview(result, client);
343+ return new UninstalledPreview(result, client, depts);
344 }
345 } else if (metadict.count(click::Preview::Actions::UNINSTALL_CLICK) != 0) {
346 return new UninstallConfirmationPreview(result);
347 } else if (metadict.count(click::Preview::Actions::CONFIRM_UNINSTALL) != 0) {
348 return new UninstallingPreview(result, client);
349 } else if (metadict.count(click::Preview::Actions::RATED) != 0) {
350- return new InstalledPreview(result, metadata, client);
351+ return new InstalledPreview(result, metadata, client, depts);
352 } else {
353 qWarning() << "preview() called with unexpected metadata. returning uninstalled preview";
354- return new UninstalledPreview(result, client);
355+ return new UninstalledPreview(result, client, depts);
356 }
357 } else {
358 // metadata.scope_data() is Null, so we return an appropriate "default" preview:
359@@ -109,9 +145,9 @@
360 return new InstalledScopePreview(result);
361 }
362 if (result["installed"].get_bool() == true) {
363- return new InstalledPreview(result, metadata, client);
364+ return new InstalledPreview(result, metadata, client, depts);
365 } else {
366- return new UninstalledPreview(result, client);
367+ return new UninstalledPreview(result, client, depts);
368 }
369 }
370
371@@ -340,15 +376,17 @@
372 reply->push(downloadErrorWidgets());
373 }
374
375-
376 // class InstallingPreview
377
378 InstallingPreview::InstallingPreview(const std::string &download_url,
379 const unity::scopes::Result &result,
380 const QSharedPointer<click::web::Client>& client,
381- const QSharedPointer<click::network::AccessManager> &nam)
382- : PreviewStrategy(result, client), download_url(download_url),
383- downloader(new click::Downloader(nam))
384+ const QSharedPointer<click::network::AccessManager> &nam,
385+ std::shared_ptr<click::DepartmentsDb> depts)
386+ : PreviewStrategy(result, client), DepartmentUpdater(depts),
387+ download_url(download_url),
388+ downloader(new click::Downloader(nam)),
389+ depts_db(depts)
390 {
391 }
392
393@@ -383,7 +421,8 @@
394 default:
395 std::string object_path = rc.first;
396 qDebug() << "Successfully created UDM Download.";
397- populateDetails([this, reply, object_path](const PackageDetails &details){
398+ populateDetails([this, reply, object_path](const PackageDetails &details) {
399+ store_department(details);
400 reply->push(headerWidgets(details));
401 reply->push(progressBarWidget(object_path));
402 reply->push(descriptionWidgets(details));
403@@ -416,13 +455,14 @@
404 return widgets;
405 }
406
407-
408 // class InstalledPreview
409
410 InstalledPreview::InstalledPreview(const unity::scopes::Result& result,
411 const unity::scopes::ActionMetadata& metadata,
412- const QSharedPointer<click::web::Client>& client)
413+ const QSharedPointer<click::web::Client>& client,
414+ const std::shared_ptr<click::DepartmentsDb>& depts)
415 : PreviewStrategy(result, client),
416+ DepartmentUpdater(depts),
417 metadata(metadata)
418 {
419 }
420@@ -485,6 +525,7 @@
421 }
422 getApplicationUri(manifest, [this, reply, manifest, app_name, &review](const std::string& uri) {
423 populateDetails([this, reply, uri, manifest, app_name, &review](const PackageDetails &details){
424+ store_department(details);
425 reply->push(headerWidgets(details));
426 reply->push(createButtons(uri, manifest));
427 reply->push(descriptionWidgets(details));
428@@ -685,8 +726,10 @@
429 // class UninstalledPreview
430
431 UninstalledPreview::UninstalledPreview(const unity::scopes::Result& result,
432- const QSharedPointer<click::web::Client>& client)
433- : PreviewStrategy(result, client)
434+ const QSharedPointer<click::web::Client>& client,
435+ const std::shared_ptr<click::DepartmentsDb>& depts)
436+ : PreviewStrategy(result, client),
437+ DepartmentUpdater(depts)
438 {
439 }
440
441@@ -699,6 +742,7 @@
442 qDebug() << "in UninstalledPreview::run, about to populate details";
443
444 populateDetails([this, reply](const PackageDetails &details){
445+ store_department(details);
446 reply->push(headerWidgets(details));
447 reply->push(uninstalledActionButtonWidgets(details));
448 reply->push(descriptionWidgets(details));
449@@ -748,7 +792,7 @@
450 // TODO: this class should be removed once uninstall() is handled elsewhere.
451 UninstallingPreview::UninstallingPreview(const unity::scopes::Result& result,
452 const QSharedPointer<click::web::Client>& client)
453- : UninstalledPreview(result, client)
454+ : UninstalledPreview(result, client, nullptr)
455 {
456 }
457 UninstallingPreview::~UninstallingPreview()
458
459=== modified file 'libclickscope/click/preview.h'
460--- libclickscope/click/preview.h 2014-06-18 04:39:23 +0000
461+++ libclickscope/click/preview.h 2014-07-15 14:51:22 +0000
462@@ -49,6 +49,18 @@
463
464 class Manifest;
465 class PreviewStrategy;
466+class DepartmentsDb;
467+
468+class DepartmentUpdater
469+{
470+protected:
471+ DepartmentUpdater(const std::shared_ptr<click::DepartmentsDb>& depts);
472+ virtual ~DepartmentUpdater() = default;
473+ void store_department(const PackageDetails& pkg);
474+
475+private:
476+ std::shared_ptr<click::DepartmentsDb> depts;
477+};
478
479 class Preview : public unity::scopes::PreviewQueryBase
480 {
481@@ -57,7 +69,8 @@
482 PreviewStrategy* choose_strategy(const unity::scopes::Result& result,
483 const unity::scopes::ActionMetadata& metadata,
484 const QSharedPointer<web::Client> &client,
485- const QSharedPointer<click::network::AccessManager>& nam);
486+ const QSharedPointer<click::network::AccessManager>& nam,
487+ std::shared_ptr<click::DepartmentsDb> depts);
488 public:
489 struct Actions
490 {
491@@ -82,7 +95,8 @@
492 Preview(const unity::scopes::Result& result,
493 const unity::scopes::ActionMetadata& metadata,
494 const QSharedPointer<click::web::Client>& client,
495- const QSharedPointer<click::network::AccessManager>& nam);
496+ const QSharedPointer<click::network::AccessManager>& nam,
497+ std::shared_ptr<click::DepartmentsDb> depts);
498 // From unity::scopes::PreviewQuery
499 void cancelled() override;
500 virtual void run(unity::scopes::PreviewReplyProxy const& reply) override;
501@@ -134,13 +148,14 @@
502 void run(unity::scopes::PreviewReplyProxy const& reply) override;
503 };
504
505-class InstallingPreview : public PreviewStrategy
506+class InstallingPreview : public PreviewStrategy, public DepartmentUpdater
507 {
508 public:
509 InstallingPreview(std::string const& download_url,
510 const unity::scopes::Result& result,
511 const QSharedPointer<click::web::Client>& client,
512- const QSharedPointer<click::network::AccessManager>& nam);
513+ const QSharedPointer<click::network::AccessManager>& nam,
514+ std::shared_ptr<click::DepartmentsDb> depts);
515
516 virtual ~InstallingPreview();
517
518@@ -150,15 +165,17 @@
519 virtual scopes::PreviewWidgetList progressBarWidget(const std::string& object_path);
520 std::string download_url;
521 QSharedPointer<click::Downloader> downloader;
522+ std::shared_ptr<click::DepartmentsDb> depts_db;
523 void startLauncherAnimation(const PackageDetails& details);
524 };
525
526-class InstalledPreview : public PreviewStrategy
527+class InstalledPreview : public PreviewStrategy, public DepartmentUpdater
528 {
529 public:
530 InstalledPreview(const unity::scopes::Result& result,
531 const unity::scopes::ActionMetadata& metadata,
532- const QSharedPointer<click::web::Client>& client);
533+ const QSharedPointer<click::web::Client>& client,
534+ const std::shared_ptr<click::DepartmentsDb>& depts);
535
536 virtual ~InstalledPreview();
537
538@@ -204,11 +221,12 @@
539
540 };
541
542-class UninstalledPreview : public PreviewStrategy
543+class UninstalledPreview : public PreviewStrategy, public DepartmentUpdater
544 {
545 public:
546 UninstalledPreview(const unity::scopes::Result& result,
547- const QSharedPointer<click::web::Client>& client);
548+ const QSharedPointer<click::web::Client>& client,
549+ const std::shared_ptr<click::DepartmentsDb>& depts);
550
551 virtual ~UninstalledPreview();
552
553
554=== modified file 'libclickscope/tests/CMakeLists.txt'
555--- libclickscope/tests/CMakeLists.txt 2014-07-11 21:56:59 +0000
556+++ libclickscope/tests/CMakeLists.txt 2014-07-15 14:51:22 +0000
557@@ -26,7 +26,6 @@
558 test_bootstrap.cpp
559 test_configuration.cpp
560 test_departments.cpp
561- test_departments-db.cpp
562 test_download_manager.cpp
563 test_index.cpp
564 test_interface.cpp
565@@ -34,6 +33,8 @@
566 test_reviews.cpp
567 test_smartconnect.cpp
568 test_webclient.cpp
569+ test_departments.cpp
570+ test_departments-db.cpp
571
572 ${CMAKE_CURRENT_BINARY_DIR}/test_data.cpp
573 )
574
575=== modified file 'libclickscope/tests/fake_json.cpp'
576--- libclickscope/tests/fake_json.cpp 2014-07-11 09:15:22 +0000
577+++ libclickscope/tests/fake_json.cpp 2014-07-15 14:51:22 +0000
578@@ -412,6 +412,7 @@
579 "clickindex:department": [
580 {
581 "name": "Games",
582+ "slug": "games",
583 "_links": {
584 "self": {
585 "href": "https://search.apps.ubuntu.com/api/v1/departments/Games"
586@@ -421,6 +422,7 @@
587 "clickindex:department": [
588 {
589 "name": "Board Games",
590+ "slug": "board_games",
591 "_links": {
592 "self": {
593 "href": "https://search.apps.ubuntu.com/api/v1/departments/Games/Board+Games"
594@@ -432,6 +434,7 @@
595 },
596 {
597 "name": "Graphics",
598+ "slug": "graphics",
599 "_links": {
600 "self": {
601 "href": "https://search.apps.ubuntu.com/api/v1/departments/Graphics"
602@@ -441,6 +444,7 @@
603 "clickindex:department": [
604 {
605 "name": "Drawing",
606+ "slug": "graphics_drawing",
607 "_links": {
608 "self": {
609 "href": "https://search.apps.ubuntu.com/api/v1/departments/Graphics/Drawing"
610@@ -452,6 +456,7 @@
611 },
612 {
613 "name": "Internet",
614+ "slug": "internet",
615 "_links": {
616 "self": {
617 "href": "https://search.apps.ubuntu.com/api/v1/departments/Internet"
618@@ -461,6 +466,7 @@
619 "clickindex:department": [
620 {
621 "name": "Chat",
622+ "slug": "internet_chat",
623 "_links": {
624 "self": {
625 "href": "https://search.apps.ubuntu.com/api/v1/departments/Internet/Chat"
626@@ -469,6 +475,7 @@
627 },
628 {
629 "name": "Mail",
630+ "slug": "internet_mail",
631 "_links": {
632 "self": {
633 "href": "https://search.apps.ubuntu.com/api/v1/departments/Internet/Mail"
634@@ -477,6 +484,7 @@
635 },
636 {
637 "name": "Web Browsers",
638+ "slug": "internet_web",
639 "_links": {
640 "self": {
641 "href": "https://search.apps.ubuntu.com/api/v1/departments/Internet/Web+Browsers"
642@@ -508,6 +516,7 @@
643 "clickindex:department": [
644 {
645 "name": "Games",
646+ "slug": "games",
647 "_links": {
648 "self": {
649 "href": "https://search.apps.ubuntu.com/api/v1/departments/Games"
650@@ -622,5 +631,3 @@
651 }
652 }
653 )foo";
654-
655-
656
657=== modified file 'libclickscope/tests/test_departments.cpp'
658--- libclickscope/tests/test_departments.cpp 2014-06-23 11:33:55 +0000
659+++ libclickscope/tests/test_departments.cpp 2014-07-15 14:51:22 +0000
660@@ -40,7 +40,7 @@
661 auto it = depts.cbegin();
662 {
663 auto dep = *it;
664- EXPECT_EQ("Games", dep->id());
665+ EXPECT_EQ("games", dep->id());
666 EXPECT_EQ("Games", dep->name());
667 EXPECT_EQ("https://search.apps.ubuntu.com/api/v1/departments/Games", dep->href());
668 EXPECT_FALSE(dep->has_children_flag());
669@@ -52,7 +52,7 @@
670 {
671 ++it;
672 auto dep = *it;
673- EXPECT_EQ("Graphics", dep->id());
674+ EXPECT_EQ("graphics", dep->id());
675 EXPECT_EQ("Graphics", dep->name());
676 EXPECT_EQ("https://search.apps.ubuntu.com/api/v1/departments/Graphics", dep->href());
677 EXPECT_FALSE(dep->has_children_flag());
678@@ -64,7 +64,7 @@
679 {
680 ++it;
681 auto dep = *it;
682- EXPECT_EQ("Internet", dep->id());
683+ EXPECT_EQ("internet", dep->id());
684 EXPECT_EQ("Internet", dep->name());
685 EXPECT_EQ("https://search.apps.ubuntu.com/api/v1/departments/Internet", dep->href());
686 EXPECT_FALSE(dep->has_children_flag());
687
688=== modified file 'libclickscope/tests/test_index.cpp'
689--- libclickscope/tests/test_index.cpp 2014-06-26 18:57:08 +0000
690+++ libclickscope/tests/test_index.cpp 2014-07-15 14:51:22 +0000
691@@ -342,7 +342,8 @@
692 {"sshot1", "sshot2"},
693 177582,
694 "0.2",
695- "None"
696+ "None",
697+ "tools"
698 };
699 EXPECT_CALL(*this, details_callback(fake_details, _)).Times(1);
700 response->replyFinished();
701@@ -389,7 +390,8 @@
702 {"sshot1", "sshot2"},
703 177582,
704 "0.2",
705- "None"
706+ "None",
707+ "tools"
708 };
709
710 EXPECT_CALL(*this, details_callback(fake_details, _)).Times(1);
711
712=== modified file 'scope/clickapps/apps-scope.cpp'
713--- scope/clickapps/apps-scope.cpp 2014-07-03 13:12:08 +0000
714+++ scope/clickapps/apps-scope.cpp 2014-07-15 14:51:22 +0000
715@@ -86,7 +86,7 @@
716 unity::scopes::PreviewQueryBase::UPtr click::Scope::preview(const unity::scopes::Result& result,
717 const unity::scopes::ActionMetadata& metadata) {
718 qDebug() << "Scope::preview() called.";
719- return scopes::PreviewQueryBase::UPtr{new click::Preview(result, metadata, client, nam)};
720+ return scopes::PreviewQueryBase::UPtr{new click::Preview(result, metadata, client, nam, nullptr)};
721 }
722
723
724
725=== modified file 'scope/clickstore/CMakeLists.txt'
726--- scope/clickstore/CMakeLists.txt 2014-06-23 15:00:28 +0000
727+++ scope/clickstore/CMakeLists.txt 2014-07-15 14:51:22 +0000
728@@ -1,6 +1,7 @@
729 SET (CMAKE_INCLUDE_CURRENT_DIR ON)
730 SET (CMAKE_AUTOMOC ON)
731 find_package (Qt5Core REQUIRED)
732+find_package (Qt5Sql REQUIRED)
733 pkg_check_modules(JSON_CPP REQUIRED jsoncpp)
734
735 add_definitions(
736@@ -19,7 +20,7 @@
737 ${JSON_CPP_INCLUDE_DIRS}
738 )
739
740-qt5_use_modules (${STORE_LIB_UNVERSIONED} Network)
741+qt5_use_modules (${STORE_LIB_UNVERSIONED} Network Sql)
742
743 target_link_libraries (${STORE_LIB_UNVERSIONED}
744 ${SCOPE_LIB_NAME}
745
746=== modified file 'scope/clickstore/store-query.cpp'
747--- scope/clickstore/store-query.cpp 2014-07-11 20:56:01 +0000
748+++ scope/clickstore/store-query.cpp 2014-07-15 14:51:22 +0000
749@@ -32,6 +32,7 @@
750 #include "store-query.h"
751 #include "store-scope.h"
752 #include <click/qtbridge.h>
753+#include <click/departments-db.h>
754
755 #include <click/key_file_locator.h>
756
757@@ -114,25 +115,29 @@
758 struct click::Query::Private
759 {
760 Private(click::Index& index, click::DepartmentLookup& depts,
761+ std::shared_ptr<click::DepartmentsDb> depts_db,
762 click::HighlightList& highlights, const scopes::SearchMetadata& metadata)
763 : index(index),
764 department_lookup(depts),
765+ depts_db(depts_db),
766 highlights(highlights),
767 meta(metadata)
768 {
769 }
770 click::Index& index;
771 click::DepartmentLookup& department_lookup;
772+ std::shared_ptr<click::DepartmentsDb> depts_db;
773 click::HighlightList& highlights;
774 scopes::SearchMetadata meta;
775 click::web::Cancellable search_operation;
776 };
777
778 click::Query::Query(unity::scopes::CannedQuery const& query, click::Index& index, click::DepartmentLookup& depts,
779+ std::shared_ptr<click::DepartmentsDb> depts_db,
780 click::HighlightList& highlights,
781 scopes::SearchMetadata const& metadata)
782 : unity::scopes::SearchQueryBase(query, metadata),
783- impl(new Private(index, depts, highlights, metadata))
784+ impl(new Private(index, depts, depts_db, highlights, metadata))
785 {
786 }
787
788@@ -234,6 +239,21 @@
789 root->set_subdepartments(departments);
790 }
791
792+// recursively store all departments in the departments database
793+void click::Query::store_departments(const click::DepartmentList& depts)
794+{
795+ assert(impl->depts_db);
796+
797+ try
798+ {
799+ impl->depts_db->store_departments(depts, search_metadata().locale());
800+ }
801+ catch (const std::exception &e)
802+ {
803+ qWarning() << "Failed to update database: " << QString::fromStdString(e.what());
804+ }
805+}
806+
807 void click::Query::push_package(const scopes::SearchReplyProxy& searchReply, scopes::Category::SCPtr category, const PackageSet &installedPackages, const Package& pkg)
808 {
809 qDebug() << "pushing result" << QString::fromStdString(pkg.name);
810@@ -400,6 +420,16 @@
811 impl->department_lookup.rebuild(rdeps);
812 impl->highlights = highlights;
813 qDebug() << "Total number of departments:" << impl->department_lookup.size() << ", highlights:" << highlights.size();
814+
815+ if (impl->depts_db)
816+ {
817+ qDebug() << "Storing departments in the database";
818+ store_departments(deps);
819+ }
820+ else
821+ {
822+ qWarning() << "Departments db not available";
823+ }
824 }
825 else
826 {
827
828=== modified file 'scope/clickstore/store-query.h'
829--- scope/clickstore/store-query.h 2014-06-18 09:01:20 +0000
830+++ scope/clickstore/store-query.h 2014-07-15 14:51:22 +0000
831@@ -50,6 +50,7 @@
832 class Application;
833 class Index;
834 class DepartmentLookup;
835+class DepartmentsDb;
836
837 class Query : public scopes::SearchQueryBase
838 {
839@@ -76,7 +77,7 @@
840 constexpr static const char* VERSION{"version"};
841 };
842
843- Query(unity::scopes::CannedQuery const& query, click::Index& index, click::DepartmentLookup& dept_lookup, click::HighlightList& highlights,
844+ Query(unity::scopes::CannedQuery const& query, click::Index& index, click::DepartmentLookup& dept_lookup, std::shared_ptr<click::DepartmentsDb> depts_db, click::HighlightList& highlights,
845 scopes::SearchMetadata const& metadata);
846 virtual ~Query();
847
848@@ -86,6 +87,7 @@
849
850 protected:
851 virtual void populate_departments(const click::DepartmentList& depts, const std::string& current_department_id, unity::scopes::Department::SPtr &root);
852+ virtual void store_departments(const click::DepartmentList& depts);
853 virtual void push_departments(const scopes::SearchReplyProxy& searchReply, const scopes::Department::SCPtr& root);
854 virtual void add_highlights(scopes::SearchReplyProxy const& searchReply, const PackageSet& installedPackages);
855 virtual void add_available_apps(const scopes::SearchReplyProxy &searchReply, const PackageSet &installedPackages, const std::string &category);
856
857=== modified file 'scope/clickstore/store-scope.cpp'
858--- scope/clickstore/store-scope.cpp 2014-06-23 15:00:28 +0000
859+++ scope/clickstore/store-scope.cpp 2014-07-15 14:51:22 +0000
860@@ -28,6 +28,7 @@
861 */
862
863 #include <click/qtbridge.h>
864+#include <click/departments-db.h>
865 #include <click/department-lookup.h>
866 #include "store-scope.h"
867 #include "store-query.h"
868@@ -42,6 +43,7 @@
869 #include <click/click-i18n.h>
870
871 #include <logging.h>
872+#include <iostream>
873
874 bool click::Scope::old_api = false;
875
876@@ -52,6 +54,14 @@
877 index.reset(new click::Index(client));
878 depts.reset(new click::DepartmentLookup());
879 highlights.reset(new click::HighlightList());
880+ try
881+ {
882+ depts_db = click::DepartmentsDb::create_db();
883+ }
884+ catch (const std::runtime_error& e)
885+ {
886+ std::cerr << "Failed to get cache directory" << std::endl;
887+ }
888 }
889
890 click::Scope::~Scope()
891@@ -94,14 +104,14 @@
892
893 scopes::SearchQueryBase::UPtr click::Scope::search(unity::scopes::CannedQuery const& q, scopes::SearchMetadata const& metadata)
894 {
895- return scopes::SearchQueryBase::UPtr(new click::Query(q, *index, *depts, *highlights, metadata));
896+ return scopes::SearchQueryBase::UPtr(new click::Query(q, *index, *depts, depts_db, *highlights, metadata));
897 }
898
899
900 unity::scopes::PreviewQueryBase::UPtr click::Scope::preview(const unity::scopes::Result& result,
901 const unity::scopes::ActionMetadata& metadata) {
902 qDebug() << "Scope::preview() called.";
903- return scopes::PreviewQueryBase::UPtr{new click::Preview(result, metadata, client, nam)};
904+ return scopes::PreviewQueryBase::UPtr{new click::Preview(result, metadata, client, nam, depts_db)};
905 }
906
907 unity::scopes::ActivationQueryBase::UPtr click::Scope::perform_action(unity::scopes::Result const& result, unity::scopes::ActionMetadata const& metadata,
908
909=== modified file 'scope/clickstore/store-scope.h'
910--- scope/clickstore/store-scope.h 2014-06-18 18:07:39 +0000
911+++ scope/clickstore/store-scope.h 2014-07-15 14:51:22 +0000
912@@ -46,6 +46,7 @@
913 {
914
915 class DepartmentLookup;
916+class DepartmentsDb;
917
918 class Scope : public scopes::ScopeBase
919 {
920@@ -73,6 +74,7 @@
921 QSharedPointer<click::Index> index;
922 std::shared_ptr<click::DepartmentLookup> depts;
923 std::shared_ptr<click::HighlightList> highlights;
924+ std::shared_ptr<click::DepartmentsDb> depts_db;
925
926 std::string installApplication(unity::scopes::Result const& result);
927 static bool old_api;
928
929=== modified file 'scope/tests/CMakeLists.txt'
930--- scope/tests/CMakeLists.txt 2014-07-03 13:12:08 +0000
931+++ scope/tests/CMakeLists.txt 2014-07-15 14:51:22 +0000
932@@ -7,6 +7,7 @@
933 SET (CMAKE_AUTOMOC ON)
934
935 find_package(Qt5Core REQUIRED)
936+find_package(Qt5Sql REQUIRED)
937
938 include_directories (
939 ${CMAKE_SOURCE_DIR}/libclickscope
940@@ -24,8 +25,8 @@
941 test_apps_query.cpp
942 )
943
944-qt5_use_modules(${CLICKSCOPE_TESTS_TARGET} Core DBus Network Test)
945-qt5_use_modules(${APPS_SCOPE_TESTS_TARGET} Core DBus Network Test)
946+qt5_use_modules(${CLICKSCOPE_TESTS_TARGET} Core DBus Network Test Sql)
947+qt5_use_modules(${APPS_SCOPE_TESTS_TARGET} Core DBus Network Test Sql)
948
949 target_link_libraries(${CLICKSCOPE_TESTS_TARGET}
950 ${STORE_LIB_UNVERSIONED}
951
952=== modified file 'scope/tests/test_query.cpp'
953--- scope/tests/test_query.cpp 2014-07-11 14:55:30 +0000
954+++ scope/tests/test_query.cpp 2014-07-15 14:51:22 +0000
955@@ -36,6 +36,7 @@
956 #include "click/qtbridge.h"
957 #include "clickstore/store-query.h"
958 #include "click/application.h"
959+#include "click/departments-db.h"
960 #include "test_helpers.h"
961
962 #include <tests/mock_network_access_manager.h>
963@@ -53,12 +54,27 @@
964
965 namespace
966 {
967+
968+class MockDepartmentsDb : public click::DepartmentsDb
969+{
970+public:
971+ MockDepartmentsDb(const std::string& name)
972+ : click::DepartmentsDb(name)
973+ {
974+ }
975+
976+ MOCK_METHOD2(store_package_mapping, void(const std::string&, const std::string&));
977+ MOCK_METHOD2(store_department_mapping, void(const std::string&, const std::string&));
978+ MOCK_METHOD3(store_department_name, void(const std::string&, const std::string&, const std::string&));
979+};
980+
981 class MockQueryBase : public click::Query {
982 public:
983 MockQueryBase(const unity::scopes::CannedQuery& query, click::Index& index,
984 click::DepartmentLookup& depts,
985+ std::shared_ptr<click::DepartmentsDb> depts_db,
986 click::HighlightList& highlights,
987- scopes::SearchMetadata const& metadata) : click::Query(query, index, depts, highlights, metadata)
988+ scopes::SearchMetadata const& metadata) : click::Query(query, index, depts, depts_db, highlights, metadata)
989 {
990
991 }
992@@ -73,8 +89,9 @@
993 public:
994 MockQuery(const unity::scopes::CannedQuery& query, click::Index& index,
995 click::DepartmentLookup& depts,
996+ std::shared_ptr<click::DepartmentsDb> depts_db,
997 click::HighlightList& highlights,
998- scopes::SearchMetadata const& metadata) : MockQueryBase(query, index, depts, highlights, metadata)
999+ scopes::SearchMetadata const& metadata) : MockQueryBase(query, index, depts, depts_db, highlights, metadata)
1000 {
1001
1002 }
1003@@ -99,8 +116,9 @@
1004 public:
1005 MockQueryRun(const unity::scopes::CannedQuery& query, click::Index& index,
1006 click::DepartmentLookup& depts,
1007+ std::shared_ptr<click::DepartmentsDb> depts_db,
1008 click::HighlightList& highlights,
1009- scopes::SearchMetadata const& metadata) : MockQueryBase(query, index, depts, highlights, metadata)
1010+ scopes::SearchMetadata const& metadata) : MockQueryBase(query, index, depts, depts_db, highlights, metadata)
1011 {
1012
1013 }
1014@@ -123,7 +141,7 @@
1015 scopes::SearchMetadata metadata("en_EN", "phone");
1016 PackageSet no_installed_packages;
1017 const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1018- MockQuery q(query, mock_index, dept_lookup, highlights, metadata);
1019+ MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata);
1020 EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _)).Times(1);
1021
1022 scopes::testing::MockSearchReply mock_reply;
1023@@ -148,7 +166,7 @@
1024 scopes::SearchMetadata metadata("en_EN", "phone");
1025 PackageSet no_installed_packages;
1026 const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1027- MockQuery q(query, mock_index, dept_lookup, highlights, metadata);
1028+ MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata);
1029 EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _));
1030
1031 scopes::CategoryRenderer renderer("{}");
1032@@ -177,7 +195,7 @@
1033 scopes::SearchMetadata metadata("en_EN", "phone");
1034 PackageSet no_installed_packages;
1035 const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1036- MockQuery q(query, mock_index, dept_lookup, highlights, metadata);
1037+ MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata);
1038 EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _));
1039
1040 scopes::CategoryRenderer renderer("{}");
1041@@ -201,7 +219,7 @@
1042 scopes::SearchMetadata metadata("en_EN", "phone");
1043 PackageSet no_installed_packages;
1044 const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1045- MockQueryRun q(query, mock_index, dept_lookup, highlights, metadata);
1046+ MockQueryRun q(query, mock_index, dept_lookup, nullptr, highlights, metadata);
1047 auto reply = scopes::SearchReplyProxy();
1048 EXPECT_CALL(q, get_installed_packages()).WillOnce(Return(no_installed_packages));
1049 EXPECT_CALL(q, add_available_apps(reply, no_installed_packages, _));
1050@@ -226,7 +244,7 @@
1051 click::DepartmentLookup dept_lookup;
1052 click::HighlightList highlights;
1053 const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1054- MockQuery q(query, mock_index, dept_lookup, highlights, metadata);
1055+ MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata);
1056 EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _));
1057
1058 scopes::CategoryRenderer renderer("{}");
1059@@ -256,7 +274,7 @@
1060 click::DepartmentLookup dept_lookup;
1061 click::HighlightList highlights;
1062 const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1063- MockQuery q(query, mock_index, dept_lookup, highlights, metadata);
1064+ MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata);
1065 EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _));
1066
1067 scopes::CategoryRenderer renderer("{}");
1068@@ -270,6 +288,39 @@
1069 q.wrap_add_available_apps(reply, one_installed_package, FAKE_CATEGORY_TEMPLATE);
1070 }
1071
1072+TEST(QueryTest, testDepartmentsDbIsUpdated)
1073+{
1074+ auto dept1 = std::make_shared<click::Department>("1", "Department one", "http://one.com", true);
1075+ dept1->set_subdepartments({
1076+ std::make_shared<click::Department>("1-1", "Department two", "http://two.com", false),
1077+ std::make_shared<click::Department>("1-2", "Department three", "http://three.com", false)
1078+ });
1079+ DepartmentList init_departments({dept1});
1080+ auto depts_db = std::make_shared<MockDepartmentsDb>("query-tests.db");
1081+
1082+ EXPECT_CALL(*depts_db, store_department_name(_, _, _)).Times(3);
1083+ EXPECT_CALL(*depts_db, store_department_mapping(_, _)).Times(2);
1084+
1085+ MockIndex mock_index(click::Packages(), click::DepartmentList(), init_departments);
1086+ scopes::SearchMetadata metadata("en_EN", "phone");
1087+ PackageSet one_installed_package {
1088+ {"org.example.app2", "0.2"}
1089+ };
1090+ click::DepartmentLookup dept_lookup;
1091+ click::HighlightList highlights;
1092+ const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1093+ MockQuery q(query, mock_index, dept_lookup, depts_db, highlights, metadata);
1094+ EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _));
1095+
1096+ scopes::CategoryRenderer renderer("{}");
1097+ auto ptrCat = std::make_shared<FakeCategory>("id", "", "", renderer);
1098+ EXPECT_CALL(q, register_category(_, _, _, _, _)).Times(2).WillRepeatedly(Return(ptrCat));
1099+
1100+ scopes::testing::MockSearchReply mock_reply;
1101+ scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){});
1102+ q.wrap_add_available_apps(reply, one_installed_package, FAKE_CATEGORY_TEMPLATE);
1103+}
1104+
1105 class FakeInterface : public click::Interface
1106 {
1107 public:
1108@@ -286,7 +337,7 @@
1109 click::DepartmentLookup dept_lookup;
1110 click::HighlightList highlights;
1111 const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1112- MockQuery q(query, mock_index, dept_lookup, highlights, metadata);
1113+ MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata);
1114 PackageSet installed_packages{{"package_1", "0.1"}};
1115
1116 FakeInterface fake_interface;
1117
1118=== added directory 'tools'
1119=== added file 'tools/CMakeLists.txt'
1120--- tools/CMakeLists.txt 1970-01-01 00:00:00 +0000
1121+++ tools/CMakeLists.txt 2014-07-15 14:51:22 +0000
1122@@ -0,0 +1,1 @@
1123+add_subdirectory(init-departments)
1124
1125=== added directory 'tools/init-departments'
1126=== added file 'tools/init-departments/CMakeLists.txt'
1127--- tools/init-departments/CMakeLists.txt 1970-01-01 00:00:00 +0000
1128+++ tools/init-departments/CMakeLists.txt 2014-07-15 14:51:22 +0000
1129@@ -0,0 +1,19 @@
1130+set (CMAKE_INCLUDE_CURRENT_DIR ON)
1131+set (INITDEPTS init-departments)
1132+
1133+find_package (Qt5Core REQUIRED)
1134+
1135+include_directories (
1136+ ${CMAKE_SOURCE_DIR}/libclickscope
1137+ ${JSON_CPP_INCLUDE_DIRS}
1138+ )
1139+
1140+add_executable (${INITDEPTS}
1141+ init-departments.cpp
1142+ )
1143+
1144+qt5_use_modules (${INITDEPTS} Network Sql)
1145+
1146+target_link_libraries(${INITDEPTS}
1147+ ${SCOPE_LIB_NAME}
1148+ )
1149
1150=== added file 'tools/init-departments/README'
1151--- tools/init-departments/README 1970-01-01 00:00:00 +0000
1152+++ tools/init-departments/README 2014-07-15 14:51:22 +0000
1153@@ -0,0 +1,10 @@
1154+This tool creates/updates the list of departments and package:department mapping
1155+for currently installed apps, and stores it in a sqlite database file.
1156+This db should then be put in the unity-scope-click-departmentsdb package or manually
1157+copied to the location where click scope expects it to be (~/.cache/click-departments.db).
1158+
1159+Usage:
1160+init-departments DBFILE LOCALE1 [LOCALE2 ...]
1161+
1162+for example:
1163+init-departments click-departments.db "" pl_PL de_DE
1164
1165=== added file 'tools/init-departments/init-departments.cpp'
1166--- tools/init-departments/init-departments.cpp 1970-01-01 00:00:00 +0000
1167+++ tools/init-departments/init-departments.cpp 2014-07-15 14:51:22 +0000
1168@@ -0,0 +1,276 @@
1169+/*
1170+ * Copyright (C) 2014 Canonical Ltd.
1171+ *
1172+ * This program is free software: you can redistribute it and/or modify it
1173+ * under the terms of the GNU General Public License version 3, as published
1174+ * by the Free Software Foundation.
1175+ *
1176+ * This program is distributed in the hope that it will be useful, but
1177+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1178+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1179+ * PURPOSE. See the GNU General Public License for more details.
1180+ *
1181+ * You should have received a copy of the GNU General Public License along
1182+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1183+ *
1184+ * In addition, as a special exception, the copyright holders give
1185+ * permission to link the code of portions of this program with the
1186+ * OpenSSL library under certain conditions as described in each
1187+ * individual source file, and distribute linked combinations
1188+ * including the two.
1189+ * You must obey the GNU General Public License in all respects
1190+ * for all of the code used other than OpenSSL. If you modify
1191+ * file(s) with this exception, you may extend this exception to your
1192+ * version of the file(s), but you are not obligated to do so. If you
1193+ * do not wish to do so, delete this exception statement from your
1194+ * version. If you delete this exception statement from all source
1195+ * files in the program, then also delete it here.
1196+ */
1197+
1198+#include <click/interface.h>
1199+#include <click/key_file_locator.h>
1200+#include <click/index.h>
1201+#include <click/webclient.h>
1202+#include <click/network_access_manager.h>
1203+#include <click/qtbridge.h>
1204+#include <click/departments-db.h>
1205+#include <future>
1206+#include <iostream>
1207+#include <QDebug>
1208+#include <QtGlobal>
1209+
1210+enum
1211+{
1212+ // invalid arguments
1213+ DEPTS_ERROR_ARG = 1,
1214+
1215+ // network request errors
1216+ DEPTS_ERROR_NETWORK = 2,
1217+
1218+ // sqlite db errors
1219+ DEPTS_ERROR_DB = 5,
1220+
1221+ // click errors
1222+ DEPTS_ERROR_CLICK_PARSE = 10,
1223+ DEPTS_ERROR_CLICK_CALL = 11,
1224+ DEPTS_ERROR_CLICK_UNKNOWN = 12
1225+};
1226+
1227+std::list<std::pair<std::string, std::string>> NON_CLICK_APPS = {
1228+ {"address-book-app.desktop", "accessories"},
1229+ {"dialer-app.desktop", "accessories"},
1230+ {"mediaplayer-app.desktop", "sound-video"},
1231+ {"messaging-app.desktop", "accessories"},
1232+ {"ubuntu-system-settings.desktop", "accessories"},
1233+ {"webbrowser-app.desktop", "web-browsers"}
1234+};
1235+
1236+QSharedPointer<click::KeyFileLocator> keyFileLocator(new click::KeyFileLocator());
1237+click::Interface iface(keyFileLocator);
1238+
1239+void noDebug(QtMsgType, const QMessageLogContext&, const QString&) {}
1240+
1241+int main(int argc, char **argv)
1242+{
1243+ if (argc < 3)
1244+ {
1245+ std::cerr << "Usage: " << argv[0] << " DBFILE LOCALE1 [LOCALE2 ...]" << std::endl;
1246+ return DEPTS_ERROR_ARG;
1247+ }
1248+
1249+ const std::string dbfile(argv[1]);
1250+ const std::set<std::string> locales(argv + 2, argv + argc);
1251+
1252+ if (!getenv("INIT_DEPARTMENTS_DEBUG"))
1253+ qInstallMessageHandler(noDebug);
1254+
1255+ std::unique_ptr<click::DepartmentsDb> db;
1256+ try
1257+ {
1258+ db.reset(new click::DepartmentsDb(dbfile));
1259+ }
1260+ catch (const std::exception &e)
1261+ {
1262+ std::cerr << "Failed to open departments database " << dbfile << std::endl;
1263+ return DEPTS_ERROR_DB;
1264+ }
1265+
1266+ auto nam = QSharedPointer<click::network::AccessManager>(new click::network::AccessManager());
1267+ auto client = QSharedPointer<click::web::Client>(new click::web::Client(nam));
1268+ click::Index index(client);
1269+ std::vector<click::web::Cancellable> cnc;
1270+
1271+ std::promise<void> qt_ready;
1272+ std::promise<void> bootstrap_ready;
1273+ std::promise<click::PackageSet> pkgs_ready;
1274+ auto qt_ready_ft = qt_ready.get_future();
1275+ auto bootstrap_ft = bootstrap_ready.get_future();
1276+ auto pkgs_ft = pkgs_ready.get_future();
1277+
1278+ std::atomic<int> return_val(0);
1279+ std::atomic<std::vector<std::string>::size_type> num_of_locales(locales.size());
1280+ std::atomic<click::PackageSet::size_type> num_of_pkgs(0);
1281+
1282+ //
1283+ // a thread that does bootstrap request
1284+ std::thread net_thread([&]() {
1285+ qt_ready_ft.get();
1286+
1287+ for (auto const locale: locales)
1288+ {
1289+ qt::core::world::enter_with_task([&return_val, &bootstrap_ready, &index, &cnc, &num_of_locales, &db, locale]() {
1290+
1291+ std::cout << "Getting departments for locale '" << locale << "'" << std::endl;
1292+ 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) {
1293+ std::cout << "Bootstrap call for locale '" << locale << "' finished" << std::endl;
1294+
1295+ if (error == click::Index::Error::NoError)
1296+ {
1297+ try
1298+ {
1299+ db->store_departments(depts, locale);
1300+ std::cout << "Stored departments for locale '" << locale << "'" << std::endl;
1301+ }
1302+ catch (const std::exception& e)
1303+ {
1304+ std::cerr << "Failed to update departments database: " << e.what() << std::endl;
1305+ return_val = DEPTS_ERROR_DB;
1306+ }
1307+
1308+ if (--num_of_locales == 0)
1309+ {
1310+ std::cout << "All locales processed" << std::endl;
1311+ bootstrap_ready.set_value();
1312+ }
1313+ }
1314+ else
1315+ {
1316+ std::cerr << "Network error" << std::endl;
1317+ return_val = DEPTS_ERROR_NETWORK;
1318+ bootstrap_ready.set_value();
1319+ qt::core::world::destroy();
1320+ return;
1321+ }
1322+ }));
1323+ });
1324+ }
1325+ });
1326+
1327+ //
1328+ // a thread that iterates over all packages and performs details requests
1329+ // it initially blocks on bootstrap_ft and pkgs_ft future
1330+ // (waits until bootstrap finished and main thread gets all the click packages)
1331+ std::thread details_thread([&]() {
1332+ bootstrap_ft.get();
1333+ auto const pkgs = pkgs_ft.get();
1334+
1335+ num_of_pkgs = pkgs.size();
1336+
1337+ // if click list failed or nothing to do, then end this thread and stop Qt bridge
1338+ if (return_val != 0 || num_of_pkgs == 0)
1339+ {
1340+ std::cerr << "No packages to process or an error occurred, not fetching departments" << std::endl;
1341+ qt::core::world::destroy();
1342+ return;
1343+ }
1344+
1345+ std::cout << "Getting package details for " << num_of_pkgs << " packages" << std::endl;
1346+
1347+ //
1348+ // note: this queues requests for all the packages; the number of packages to process
1349+ // is kept in num_of_pkgs which gets decreased, the last processed package will stop Qt bridge.
1350+ for (auto const& pkg: pkgs)
1351+ {
1352+ auto const pkgname = pkg.name;
1353+
1354+ qt::core::world::enter_with_task([&return_val, &index, &cnc, &num_of_pkgs, pkgname, &db]() {
1355+
1356+ cnc.push_back(index.get_details(pkgname, [&return_val, &num_of_pkgs, pkgname, &db](const click::PackageDetails& details, click::Index::Error error) {
1357+ std::cout << "Details call for " << pkgname << " finished" << std::endl;
1358+
1359+ if (error == click::Index::Error::NoError)
1360+ {
1361+ try
1362+ {
1363+ std::cout << "Storing package department for " << pkgname << ", " << details.department << std::endl;
1364+ db->store_package_mapping(pkgname, details.department);
1365+ }
1366+ catch (const std::exception& e)
1367+ {
1368+ std::cerr << "Failed to update departments database: " << e.what() << std::endl;
1369+ return_val = DEPTS_ERROR_DB;
1370+ }
1371+ }
1372+ else
1373+ {
1374+ std::cerr << "Network error" << std::endl;
1375+ return_val = DEPTS_ERROR_NETWORK;
1376+ }
1377+ if (--num_of_pkgs == 0)
1378+ {
1379+ std::cout << "All packages processed" << std::endl;
1380+ qt::core::world::destroy();
1381+ }
1382+ }));
1383+ });
1384+ }
1385+ });
1386+
1387+ //
1388+ // enter Qt world; this blocks until qt::core:;world::destroy() gets called
1389+ qt::core::world::build_and_run(argc, argv, [&pkgs_ready, &qt_ready, &return_val]() {
1390+
1391+ qt::core::world::enter_with_task([&return_val, &pkgs_ready, &qt_ready]() {
1392+ std::cout << "Querying click for installed packages" << std::endl;
1393+ iface.get_installed_packages([&return_val, &pkgs_ready, &qt_ready](click::PackageSet pkgs, click::InterfaceError error) {
1394+ if (error == click::InterfaceError::NoError)
1395+ {
1396+ std::cout << "Found: " << pkgs.size() << " click packages" << std::endl;
1397+ }
1398+ else
1399+ {
1400+ if (error == click::InterfaceError::ParseError)
1401+ {
1402+ std::cerr << "Error parsing click output" << std::endl;
1403+ return_val = DEPTS_ERROR_CLICK_PARSE;
1404+ }
1405+ else if (error == click::InterfaceError::CallError)
1406+ {
1407+ std::cerr << "Error calling click command" << std::endl;
1408+ return_val = DEPTS_ERROR_CLICK_CALL;
1409+ }
1410+ else
1411+ {
1412+ std::cerr << "An unknown click error occured" << std::endl;
1413+ return_val = DEPTS_ERROR_CLICK_UNKNOWN;
1414+ }
1415+ }
1416+ qt_ready.set_value();
1417+ pkgs_ready.set_value(pkgs); // this unblocks net_thread
1418+ });
1419+ });
1420+ });
1421+
1422+ net_thread.join();
1423+ details_thread.join();
1424+
1425+ try
1426+ {
1427+ for (auto const& app: NON_CLICK_APPS)
1428+ {
1429+ db->store_package_mapping(app.first, app.second);
1430+ }
1431+ }
1432+ catch (const std::exception &e)
1433+ {
1434+ std::cerr << "Failed to insert non-click appsint database" << std::endl;
1435+ return DEPTS_ERROR_DB;
1436+ }
1437+
1438+ std::cout << std::endl << "Summary:" << std::endl
1439+ << "Number of department mappings: " << db->department_mapping_count() << std::endl
1440+ << "Number of department names (all locales): " << db->department_name_count() << std::endl
1441+ << "Number of applications: " << db->package_count() << std::endl;
1442+
1443+ return return_val;
1444+}

Subscribers

People subscribed via source and target branches

to all changes: