Merge lp:~rmescandon/scopecreator/rss-template-read-settings-from-metadata-take2 into lp:~scopecreator-team/scopecreator/rss-template
- rss-template-read-settings-from-metadata-take2
- Merge into rss-template
Proposed by
Roberto Mier Escandon
Status: | Merged |
---|---|
Merged at revision: | 11 |
Proposed branch: | lp:~rmescandon/scopecreator/rss-template-read-settings-from-metadata-take2 |
Merge into: | lp:~scopecreator-team/scopecreator/rss-template |
Prerequisite: | lp:~rmescandon/scopecreator/rss-template-keywords-support |
Diff against target: |
455 lines (+203/-82) 6 files modified
data/rss.canonical_rss.ini.in (+3/-0) include/scope/query.h (+23/-1) include/scope/scope.h (+9/-0) po/rss.pot (+6/-2) src/scope/query.cpp (+143/-74) src/scope/scope.cpp (+19/-5) |
To merge this branch: | bzr merge lp:~rmescandon/scopecreator/rss-template-read-settings-from-metadata-take2 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Scott Sweeny (community) | Approve | ||
Review via email: mp+267443@code.launchpad.net |
Commit message
Same features than branch
scopecreator/
but reading scope .ini file directly using QSettings instead of using Scope::metadata
This solution is working well and can be used as alternative to scopecreator/
Description of the change
Same features than branch
scopecreator/
but reading scope .ini file directly using QSettings instead of using Scope::metadata
This solution is working well and can be used as alternative to scopecreator/
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'data/rss.canonical_rss.ini.in' |
2 | --- data/rss.canonical_rss.ini.in 2015-08-08 22:18:29 +0000 |
3 | +++ data/rss.canonical_rss.ini.in 2015-08-08 22:18:29 +0000 |
4 | @@ -10,3 +10,6 @@ |
5 | PageHeader.Logo=logo.png |
6 | PageHeader.Background=color:///#e9e7e8 |
7 | PageHeader.DividerColor=#ffeb00 |
8 | +Emblem=logo.png |
9 | +DateTimeFormat="hh:mm, d MMMM" |
10 | +BigFirstResult=true |
11 | |
12 | === modified file 'include/scope/query.h' |
13 | --- include/scope/query.h 2015-08-08 22:18:29 +0000 |
14 | +++ include/scope/query.h 2015-08-08 22:18:29 +0000 |
15 | @@ -25,6 +25,9 @@ |
16 | #include <unity/scopes/SearchQueryBase.h> |
17 | #include <unity/scopes/ReplyProxyFwd.h> |
18 | |
19 | +#include <QSharedPointer> |
20 | +#include <QSettings> |
21 | + |
22 | #include <map> |
23 | |
24 | namespace scope { |
25 | @@ -40,7 +43,8 @@ |
26 | public: |
27 | Query(const unity::scopes::CannedQuery &query, |
28 | const unity::scopes::SearchMetadata &metadata, |
29 | - const std::string scope_directory); |
30 | + const std::string& scope_directory, |
31 | + const QSharedPointer<QSettings>& scopeConfig); |
32 | |
33 | ~Query() = default; |
34 | |
35 | @@ -51,6 +55,7 @@ |
36 | private: |
37 | api::Client client_; |
38 | std::string scope_directory_; |
39 | + QSharedPointer<QSettings> mScopeConfig; |
40 | |
41 | /// @brief keyword_feeds_ map correlating aggregator scopes keywords to feed urls |
42 | std::map<std::string, std::string> keyword_feeds_; |
43 | @@ -65,6 +70,23 @@ |
44 | /// from an aggregator scope. If found, it is returned a std::pair with the feed url (pair.second) and its related |
45 | /// search keyword (pair.first) |
46 | std::pair<std::string, std::string> find_url_for_keywords(const std::set<std::string>& search_keywords); |
47 | + |
48 | + /// @brief get_display_name returns the scope name to display |
49 | + std::string get_display_name(); |
50 | + |
51 | + /// @brief get_icon returns the scoe icon file path. scope_directory/icon.png if value is not set in scope config |
52 | + std::string get_icon(); |
53 | + |
54 | + /// @brief get_emblem gets the emblem by reading the appearance parameter "Emblem" value if set. If not set, |
55 | + /// scope_directory/emblem.png file is used by default |
56 | + std::string get_emblem(); |
57 | + |
58 | + /// @brief get_date_time_format returns the format to be shown for the date and time for every result shown in an |
59 | + /// aggregator scope. If this parameter is not set, the default format is 'hh:mm, d MMMM' |
60 | + std::string get_date_time_format(); |
61 | + |
62 | + /// @brief get_big_first_result returns true if wanted to have first returned value in a format with a big image and overlayed |
63 | + bool get_big_first_result(); |
64 | }; |
65 | |
66 | } |
67 | |
68 | === modified file 'include/scope/scope.h' |
69 | --- include/scope/scope.h 2015-03-25 03:17:53 +0000 |
70 | +++ include/scope/scope.h 2015-08-08 22:18:29 +0000 |
71 | @@ -28,6 +28,9 @@ |
72 | #include <unity/scopes/QueryBase.h> |
73 | #include <unity/scopes/PreviewQueryBase.h> |
74 | |
75 | +#include <QSettings> |
76 | +#include <QSharedPointer> |
77 | + |
78 | namespace scope { |
79 | |
80 | /** |
81 | @@ -62,6 +65,12 @@ |
82 | unity::scopes::SearchQueryBase::UPtr search( |
83 | unity::scopes::CannedQuery const& q, |
84 | unity::scopes::SearchMetadata const&) override; |
85 | + |
86 | +private: |
87 | + std::string scope_id_; |
88 | + QSharedPointer<QSettings> mScopeConfig; |
89 | + |
90 | + void loadScopeConfig(); |
91 | }; |
92 | |
93 | } |
94 | |
95 | === modified file 'po/rss.pot' |
96 | --- po/rss.pot 2015-08-08 22:18:29 +0000 |
97 | +++ po/rss.pot 2015-08-08 22:18:29 +0000 |
98 | @@ -8,7 +8,7 @@ |
99 | msgstr "" |
100 | "Project-Id-Version: PACKAGE VERSION\n" |
101 | "Report-Msgid-Bugs-To: \n" |
102 | -"POT-Creation-Date: 2015-08-03 17:04+0200\n" |
103 | +"POT-Creation-Date: 2015-08-09 00:05+0200\n" |
104 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
105 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
106 | "Language-Team: LANGUAGE <LL@li.org>\n" |
107 | @@ -21,6 +21,10 @@ |
108 | msgid "Vox" |
109 | msgstr "" |
110 | |
111 | -#: ../src/scope/query.cpp:266 |
112 | +#: ../src/scope/query.cpp:309 |
113 | msgid "Open" |
114 | msgstr "" |
115 | + |
116 | +#: ../src/scope/query.cpp:316 |
117 | +msgid "More from " |
118 | +msgstr "" |
119 | |
120 | === modified file 'src/scope/query.cpp' |
121 | --- src/scope/query.cpp 2015-08-08 22:18:29 +0000 |
122 | +++ src/scope/query.cpp 2015-08-08 22:18:29 +0000 |
123 | @@ -19,7 +19,6 @@ |
124 | |
125 | #include <boost/algorithm/string/trim.hpp> |
126 | #include <boost/algorithm/string/predicate.hpp> |
127 | -#include <boost/foreach.hpp> |
128 | |
129 | #include <scope/localization.h> |
130 | #include <scope/query.h> |
131 | @@ -29,6 +28,7 @@ |
132 | #include <unity/scopes/CategoryRenderer.h> |
133 | #include <unity/scopes/Department.h> |
134 | #include <unity/scopes/QueryBase.h> |
135 | +#include <unity/scopes/CannedQuery.h> |
136 | #include <unity/scopes/SearchReply.h> |
137 | #include <unity/scopes/VariantBuilder.h> |
138 | #include <unity/scopes/SearchMetadata.h> |
139 | @@ -43,8 +43,6 @@ |
140 | #include <QJsonObject> |
141 | #include <QDateTime> |
142 | |
143 | -#define foreach BOOST_FOREACH |
144 | - |
145 | namespace sc = unity::scopes; |
146 | namespace alg = boost::algorithm; |
147 | |
148 | @@ -52,6 +50,23 @@ |
149 | using namespace api; |
150 | using namespace scope; |
151 | |
152 | +std::string FIRST_NEWS_LAYOUT = R"( |
153 | +{ |
154 | + "schema-version" : 1, |
155 | + "template" : { |
156 | + "category-layout" : "grid", |
157 | + "card-size": "large", |
158 | + "overlay": true |
159 | + }, |
160 | + "components" : { |
161 | + "title" : "title", |
162 | + "art" : { |
163 | + "field": "art", |
164 | + "aspect-ratio": 2.1 |
165 | + } |
166 | + } |
167 | +} |
168 | +)"; |
169 | |
170 | std::string NEWS_LAYOUT = R"( |
171 | { |
172 | @@ -147,29 +162,32 @@ |
173 | } |
174 | |
175 | pair<string,string> Query::find_url_for_keywords(const set<string>& search_keywords) { |
176 | - foreach(string search_keyword, search_keywords) { |
177 | + for(const string& search_keyword : search_keywords) { |
178 | auto it = keyword_feeds_.find(search_keyword); |
179 | if (it != keyword_feeds_.end()) { |
180 | - string found_url = it->second; |
181 | + const string& found_url = it->second; |
182 | return std::pair<string, string>(search_keyword, found_url); |
183 | } |
184 | } |
185 | return std::pair<string, string>{}; |
186 | } |
187 | |
188 | -Query::Query(const sc::CannedQuery &query, const sc::SearchMetadata &metadata, const string scope_directory) : |
189 | - sc::SearchQueryBase(query, metadata), |
190 | - client_(), |
191 | - scope_directory_(scope_directory) { |
192 | +Query::Query(const sc::CannedQuery &query, |
193 | + const sc::SearchMetadata &search_metadata, |
194 | + const string& scope_directory, |
195 | + const QSharedPointer<QSettings>& scopeConfig) |
196 | + : sc::SearchQueryBase(query, search_metadata) |
197 | + , client_{} |
198 | + , scope_directory_{scope_directory} |
199 | + , mScopeConfig{scopeConfig} { |
200 | } |
201 | |
202 | void Query::cancelled() { |
203 | client_.cancel(); |
204 | } |
205 | |
206 | - |
207 | void Query::run(sc::SearchReplyProxy const& reply) { |
208 | - try { |
209 | + try { |
210 | // Start by getting information about the query |
211 | const sc::CannedQuery &query(sc::SearchQueryBase::query()); |
212 | |
213 | @@ -203,74 +221,105 @@ |
214 | |
215 | vector<Client::Item> results = client_.getItems(feedUrl); |
216 | |
217 | - // Register a category for the forecast |
218 | + // Register a category for the common news |
219 | auto news_cat = reply->register_category("news", |
220 | "", "", sc::CategoryRenderer(NEWS_LAYOUT)); |
221 | - |
222 | - // For each of the forecast days |
223 | - for (const auto &item : results) { |
224 | - |
225 | - // Filter by search term if we have one |
226 | - if (!query_string.empty()) { |
227 | - QString qry = QString::fromStdString(query_string); |
228 | - QString title = QString::fromStdString(item.title); |
229 | - QString summary = QString::fromStdString(item.summary); |
230 | - QString content = QString::fromStdString(item.content); |
231 | - |
232 | - if (!title.contains(qry, Qt::CaseInsensitive) && |
233 | - !summary.contains(qry, Qt::CaseInsensitive) && |
234 | - !content.contains(qry, Qt::CaseInsensitive)) { |
235 | - continue; |
236 | + auto first_news_cat = reply->register_category("first_news", |
237 | + "", "", sc::CategoryRenderer(FIRST_NEWS_LAYOUT)); |
238 | + |
239 | + if (results.size() > 0) { |
240 | + |
241 | + // Get common values for all results to avoid asking for them in every iteration |
242 | + std::string emblem = get_emblem(); |
243 | + std::string date_time_format = get_date_time_format(); |
244 | + bool show_big_result = get_big_first_result(); |
245 | + |
246 | + // For each of the results vector items |
247 | + for (const auto &item : results) { |
248 | + |
249 | + // Filter by search term if we have one |
250 | + if (!query_string.empty()) { |
251 | + QString qry = QString::fromStdString(query_string); |
252 | + QString title = QString::fromStdString(item.title); |
253 | + QString summary = QString::fromStdString(item.summary); |
254 | + QString content = QString::fromStdString(item.content); |
255 | + |
256 | + if (!title.contains(qry, Qt::CaseInsensitive) && |
257 | + !summary.contains(qry, Qt::CaseInsensitive) && |
258 | + !content.contains(qry, Qt::CaseInsensitive)) { |
259 | + continue; |
260 | + } |
261 | } |
262 | - } |
263 | - |
264 | - // Create a result |
265 | - sc::CategorisedResult res(news_cat); |
266 | - |
267 | - // We must have a URI |
268 | - res.set_uri(item.uri); |
269 | - res.set_dnd_uri(item.uri); |
270 | - |
271 | - // Set the rest of the attributes |
272 | - res.set_title(item.title); |
273 | - res.set_art(item.art); |
274 | - res["published"] = sc::Variant(item.published); |
275 | - res["author"] = sc::Variant(item.author); |
276 | - res["summary"] = sc::Variant(item.summary); |
277 | - res["content"] = sc::Variant(item.content); |
278 | - res["emblem"] = sc::Variant(scope_directory_+"/images/emblem.png"); |
279 | - |
280 | - if (res.art().empty()) { |
281 | - res.set_art(scope_directory_ + "/icon.png"); |
282 | - } |
283 | - |
284 | - // if searched from aggregated, set formatted date to subtitle in case found keyword is related with news |
285 | - if (meta.is_aggregated()) { |
286 | - if (boost::starts_with(found_keyword, "news.")) { |
287 | + |
288 | + // Create a result if having title at least |
289 | + if (!item.title.empty() && !item.uri.empty()) { |
290 | + |
291 | + // If surfacing in not aggregated mode and set big-first-result setting to true, show first result in big |
292 | + sc::Category::SCPtr cat = |
293 | + show_big_result && !meta.is_aggregated() && query_string.empty() ? |
294 | + first_news_cat : |
295 | + news_cat; |
296 | + |
297 | + show_big_result = false; |
298 | + |
299 | + sc::CategorisedResult res(cat); |
300 | + |
301 | + // We must have a URI |
302 | + res.set_uri(item.uri); |
303 | + res.set_dnd_uri(item.uri); |
304 | + |
305 | + // Set the rest of the attributes |
306 | + res.set_title(item.title); |
307 | + res.set_art(item.art); |
308 | + res["author"] = sc::Variant(item.author); |
309 | + res["summary"] = sc::Variant(item.summary); |
310 | + res["content"] = sc::Variant(item.content); |
311 | + res["emblem"] = sc::Variant(emblem); |
312 | + |
313 | + if (res.art().empty()) { |
314 | + res.set_art(get_icon()); |
315 | + } |
316 | + |
317 | QString date = QString::fromStdString(item.published); |
318 | + |
319 | if (!date.isEmpty()){ |
320 | - QDateTime ipostTime = QDateTime::fromString(date, Qt::RFC2822Date); |
321 | - QString readeableTime = ipostTime.toString("hh:mm, d MMMM"); |
322 | - res["subtitle"] = readeableTime.toStdString(); |
323 | - } |
324 | - } |
325 | - } |
326 | - |
327 | - sc::VariantBuilder actions; |
328 | - actions.add_tuple({ |
329 | - {"id", sc::Variant("open")}, |
330 | - {"label", sc::Variant(_("Open"))}, |
331 | - {"uri", sc::Variant(res.dnd_uri())} |
332 | - }); |
333 | - res["actions"] = actions.end(); |
334 | - |
335 | - // Push the result |
336 | - if (!reply->push(res)) { |
337 | - // If we fail to push, it means the query has been cancelled. |
338 | - // So don't continue; |
339 | - return; |
340 | - } |
341 | - } |
342 | + QDateTime ipostTime = QDateTime::fromString(date, Qt::ISODate); |
343 | + QString readableTime = ipostTime.toString(QString::fromStdString(date_time_format)); |
344 | + res["published"] = readableTime.toStdString(); |
345 | + } |
346 | + |
347 | + // if searched from aggregated, set formatted date to subtitle in case found keyword is related with news |
348 | + if (meta.is_aggregated()) { |
349 | + if (boost::starts_with(found_keyword, "news.")) { |
350 | + res["subtitle"] = res["published"]; |
351 | + } |
352 | + } |
353 | + |
354 | + sc::VariantBuilder actions; |
355 | + actions.add_tuple({ |
356 | + {"id", sc::Variant("open")}, |
357 | + {"label", sc::Variant(_("Open"))}, |
358 | + {"uri", sc::Variant(res.dnd_uri())} |
359 | + }); |
360 | + |
361 | + if (meta.is_aggregated()) { |
362 | + actions.add_tuple({ |
363 | + {"id", sc::Variant("more")}, |
364 | + {"label", sc::Variant(_("More from ") + get_display_name())}, |
365 | + {"uri", sc::Variant(query.to_uri())} |
366 | + }); |
367 | + } |
368 | + res["actions"] = actions.end(); |
369 | + |
370 | + // Push the result |
371 | + if (!reply->push(res)) { |
372 | + // If we fail to push, it means the query has been cancelled. |
373 | + // So don't continue; |
374 | + return; |
375 | + } |
376 | + } // end if (!item.title.empty() && !item.uri.empty()) ... |
377 | + } // end for (const auto &item : results) ... |
378 | + } // end if (results.size > 0) ... |
379 | |
380 | } catch (domain_error &e) { |
381 | // Handle exceptions being thrown by the client API |
382 | @@ -279,3 +328,23 @@ |
383 | } |
384 | } |
385 | |
386 | +string Query::get_icon() { |
387 | + return scope_directory_ + "/" + mScopeConfig->value("ScopeConfig/Icon", "/icon.png").toString().toStdString(); |
388 | +} |
389 | + |
390 | +string Query::get_display_name() { |
391 | + return mScopeConfig->value("ScopeConfig/DisplayName", "").toString().toStdString(); |
392 | +} |
393 | + |
394 | +string Query::get_emblem() { |
395 | + return scope_directory_ + "/" + mScopeConfig->value("Appearance/Emblem", "/emblem.png").toString().toStdString(); |
396 | +} |
397 | + |
398 | +string Query::get_date_time_format() { |
399 | + return mScopeConfig->value("Appearance/DateTimeFormat", "hh:mm, d MMMM").toString().toStdString(); |
400 | +} |
401 | + |
402 | +bool Query::get_big_first_result() { |
403 | + return mScopeConfig->value("Appearance/BigFirstResult", false).toBool(); |
404 | +} |
405 | + |
406 | |
407 | === modified file 'src/scope/scope.cpp' |
408 | --- src/scope/scope.cpp 2015-03-25 03:17:53 +0000 |
409 | +++ src/scope/scope.cpp 2015-08-08 22:18:29 +0000 |
410 | @@ -1,4 +1,4 @@ |
411 | -/* |
412 | +/* |
413 | * Copyright 2015 Canonical Ltd. |
414 | * |
415 | * This program is free software; you can redistribute it and/or modify |
416 | @@ -31,21 +31,35 @@ |
417 | using namespace api; |
418 | using namespace scope; |
419 | |
420 | -void Scope::start(string const&) { |
421 | +void Scope::start(string const& scope_id) { |
422 | + |
423 | + scope_id_ = scope_id; |
424 | |
425 | setlocale(LC_ALL, ""); |
426 | string translation_directory = ScopeBase::scope_directory() |
427 | + "/../share/locale/"; |
428 | bindtextdomain(GETTEXT_PACKAGE, translation_directory.c_str()); |
429 | + |
430 | + loadScopeConfig(); |
431 | } |
432 | |
433 | void Scope::stop() { |
434 | } |
435 | |
436 | +void Scope::loadScopeConfig() { |
437 | + |
438 | + std::string scope_config_file = ScopeBase::scope_directory() + "/" + scope_id_ + ".ini"; |
439 | + mScopeConfig = QSharedPointer<QSettings>::create(QString::fromStdString(scope_config_file), QSettings::IniFormat); |
440 | + |
441 | + /* |
442 | + qDebug() << "ALL KEYS:" << scope_config_->allKeys(); |
443 | + qDebug() << "ICON KEY:" << scope_config_->value("ScopeConfig/Icon").toString(); |
444 | + */ |
445 | +} |
446 | + |
447 | sc::SearchQueryBase::UPtr Scope::search(const sc::CannedQuery &query, |
448 | - const sc::SearchMetadata &metadata) { |
449 | - // Boilerplate construction of Query |
450 | - return sc::SearchQueryBase::UPtr(new Query(query, metadata, scope_directory())); |
451 | + const sc::SearchMetadata &search_metadata) { |
452 | + return sc::SearchQueryBase::UPtr(new Query(query, search_metadata, scope_directory(), mScopeConfig)); |
453 | } |
454 | |
455 | sc::PreviewQueryBase::UPtr Scope::preview(sc::Result const& result, |
I've been running a scope based on this for a few weeks now and it works flawlessly :-)
Merging.